diff --git a/4-ShellP2/questions.md b/4-ShellP2/questions.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..20348bc9a3f709574709af20437f7ba53789d877 100644 --- a/4-ShellP2/questions.md +++ b/4-ShellP2/questions.md @@ -0,0 +1,104 @@ +1. Can you think of why we use `fork/execvp` instead of just calling `execvp` directly? What value do you think the `fork` provides? + + > **Answer**: + > `fork()` creates a new child process, which allows the shell to remain running while executing a new command. + > If we called `execvp()` directly in the shell process, the shell itself would be replaced by the new program, preventing it from executing further commands. + > `fork()` make sure that: + > - The shell can continue to accept user input after running a command. + > - Parallel execution is possible when multiple processes need to run. + > - Process management (e.g., monitoring and handling child processes) is possible. + +2. What happens if the fork() system call fails? How does your implementation handle this scenario? + + > **Answer**: + > If `fork()` fails, it returns `-1`, indicating that the system could not create a new process. + > This can happen due to too many processes running. + > In my implementation, I handle this by: + > - Printing an error message using `perror("fork")`. + > - Returning an error code `ERR_EXEC_CMD` from `exect_cmd()` to indicate failure. + +3. How does execvp() find the command to execute? What system environment variable plays a role in this process? + + > **Answer**: + > `execvp()` searches for the specified command in the directories listed in the `PATH` environment variable. + > The `PATH` variable contains a colon-separated list of directories where executables are located. + > If the command is found, it execute the command. + > If the command is not found, `execvp()` returns `-1` and sets `errno` to `ENOENT` (No such file or directory). + +4. What is the purpose of calling wait() in the parent process after forking? What would happen if we didn’t call it? + + > **Answer**: + > `wait()` makes the parent process (shell) wait for the child process to complete before continuing. + > If the program didn’t call `wait()`, the child process would still exist in the process table even though it has finished execution and the shell would continue executing commands without knowing the child's exit status. + > Using `waitpid()` allows the shell to correctly track child processes and retrieve their exit statuses. + +5. In the referenced demo code we used WEXITSTATUS(). What information does this provide, and why is it important? + + > **Answer**: + > `WEXITSTATUS(status)` extracts the exit code from a terminated child process which tells us how the command executed: + > - `0` means success. + > - Nonzero values indicate errors or failures. + > Tracking the exit status is important for: + > - Handling command failures properly. + > - Debugging easier. + +6. Describe how your implementation of build_cmd_buff() handles quoted arguments. Why is this necessary? + + > **Answer**: + > Our `build_cmd_buff()` implementation ensures **quoted arguments are treated as a single argument** by: + > - Iterating through the command input character by character. + > - Detecting quoted sections and preserving spaces inside them. + > - Everything inside the quotes is treated as one argument. + > This is necessary because: + > - Standard tokenization removes extra spaces, which would break quoted strings. + > - Without this, `echo "hello world"` would be parsed as two arguments instead of one. + > - Many shell commands rely on proper argument grouping for correct execution. + +7. What changes did you make to your parsing logic compared to the previous assignment? Were there any unexpected challenges in refactoring your old code? + + > **Answer**: + > Changes made: + > - Removed pipe handling: The previous assignment’s `build_cmd_list()` function split commands based on the `|` character, treating each command separately. Since this assignment only handles single commands without pipes, the program simplified the parsing to focus on argument extraction. + > - The old version of `build_cmd_list()` used `strtok()`, which split arguments on spaces indiscriminately. + > - The new implementation properly detects quoted arguments, ensuring spaces inside quotes are preserved. + > - Stores the last command’s exit status and allows users to retrieve it using `rc`. + > **Challenges faced:** + > - Handling spaces properly: The original code relied on `strtok()`, which doesn’t correctly handle quoted arguments. I had to implement a manual character-by-character parsing approach. + + +8. For this quesiton, you need to do some research on Linux signals. You can use [this google search](https://www.google.com/search?q=Linux+signals+overview+site%3Aman7.org+OR+site%3Alinux.die.net+OR+site%3Atldp.org&oq=Linux+signals+overview+site%3Aman7.org+OR+site%3Alinux.die.net+OR+site%3Atldp.org&gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBBzc2MGowajeoAgCwAgA&sourceid=chrome&ie=UTF-8) to get started. + +- What is the purpose of signals in a Linux system, and how do they differ from other forms of interprocess communication (IPC)? + + > **Answer**: + > Signals notify processes of events asynchronously (e.g., termination, interruption). + > Unlike other mechanisms like shared memory or pipes, signals: + > - Do not carry data, they simply notify a process to take action. + > - Can be sent by the kernel (e.g., `SIGKILL` when a process exceeds memory limits). + > - Require minimal system resources. + +- Find and describe three commonly used signals (e.g., SIGKILL, SIGTERM, SIGINT). What are their typical use cases? + + > **Answer**: + > - `SIGKILL` immediately terminates a process. + > - Cannot be caught or ignored. + > - Used for forceful termination (`kill -9 PID`). + > + > - `SIGTERM` gracefully requests process termination. + > - Can be caught to allow cleanup before exit. + > - Default signal for `kill PID`. + > + > - `SIGINT` is sent when a user presses `Ctrl+C`. + > - Default behavior is to terminate the process. + > - Can be caught to prevent accidental shutdown. + +- What happens when a process receives SIGSTOP? Can it be caught or ignored like SIGINT? Why or why not? + + > **Answer**: + > `SIGSTOP` pauses a process but does not terminate it. + > - It cannot be caught, blocked, or ignored. + > - The kernel forces the process to stop. + > - Resuming requires `SIGCONT` (`kill -CONT PID`). + > + > Unlike `SIGINT`, which a process can handle or override, `SIGSTOP` is enforced by the kernel. +