Your shell forks multiple child processes when executing piped commands. How does your implementation ensure that all child processes complete before the shell continues accepting user input? What would happen if you forgot to call waitpid() on all child processes?
Our method stores the child process's PID in an array each time the shell forks it in response to a piped command. The parent runs over this array after forking each child, using waitpid() for each child. This ensures that the shell doesn't ask the user for additional input until all of the child processes have finished. All child processes would not be properly reaped and would turn into zombie processes, using up system resources, if we neglected to run waitpid() on them. Additionally, the shell may continue running while certain piped commands are still running, which might result in inconsistent state or interleaving output.
The dup2() function is used to redirect input and output file descriptors. Explain why it is necessary to close unused pipe ends after calling dup2(). What could go wrong if you leave pipes open?
answer here
A file descriptor is duplicated using the dup2() function, while the original descriptor is left open. Since the matching end of the pipe will never get an end-of-file (EOF) signal if the unused pipe ends are left open after duplicating, it is imperative that they be closed. Processes waiting for input (or output) may become endlessly blocked as a result. Furthermore, keeping pipe ends open might gradually deplete the available file descriptors and waste system resources.
Your shell recognizes built-in commands (cd, exit, dragon). Unlike external commands, built-in commands do not require execvp(). Why is cd implemented as a built-in rather than an external command? What challenges would arise if cd were implemented as an external process?
answer here
Since the cd command needs to change the shell's own working directory, it is implemented as a built-in. The modification would only impact the child process if cd were run as an external command in that child process; the parent shell would stay in its original directory after the child process ended. The goal of switching directories in the interactive shell would be negated. The environment of the shell process itself is changed appropriately when CD is implemented as a built-in.
Currently, your shell supports a fixed number of piped commands (CMD_MAX). How would you modify your implementation to allow an arbitrary number of piped commands while still handling memory allocation efficiently? What trade-offs would you need to consider?
Instead of utilizing a fixed-size array to hold the command structures and child PIDs, you could dynamically allocate memory (using operations like malloc() and realloc()) to support an arbitrary number of piped commands. With this method, pipelines of any length may be handled by the shell. Increasing code complexity, the burden of dynamic memory management, and the necessity of carefully managing failures and resource cleaning to prevent memory leaks are some of the trade-offs. If very lengthy pipelines are employed, you would also need to think about the performance consequences. In order to avoid resource depletion, you would wish to set a sensible limit.