So far, your shell just runs external programs. Try the following interesting programs from your shell:
- top (hit 'q' to exit)
You can do a lot with your shell already! Next we'll add some built-in commands that must be implemented by the shell itself.
Add support for:
- background processing via the ampersand operator
- the 'jobs' shell command: list the pids of all living child processes (i.e. not ones that have terminated)
- "reaping" of "zombie" processes
- the 'cd' shell command: change the shell's working directory
mysh$ sleep 1000 &
mysh: started job 1209
mysh$ kill 1209
mysh: job 1209 exited with status 0
mysh$ cd /new/working/directory
file1 file2 file3
The ampersand should be the last token in a command. It means "run this command in the background".
- When backgrounding, fork and exec but don't wait.
- When backgrounding, close the child process's stdin to prevent the backgrounded process from stealing your terminal/keyboard input. To do this, use the following commands in the child process:
- fclose(stdin); // close child's stdin
- fopen("/dev/null", "r"); // open a new stdin that is always empty
- execvp(cmd, argv);
- Each time you create a background process, store its pid in a list. To implement the 'jobs' command, iterate thru the list and print out the pids which are still alive. You can tell which ones are still alive by checking kill(pid,0) == 0.
- Each time your shell reads a line of input (even if it is empty), you should check for "zombie" processes and "reap" them. Do this by looping through your list of child pids and checking waitpid(pid,&status,WNOHANG) == pid. In this form, waitpid will reap zombies. If you call waitpid on a living processes (not a zombie), it will return 0 right away (or -1 if the pid is no longer valid).
- For the 'cd' command, use chdir()
- fclose(stdin): closes standard input and frees file descriptor 0 (stdin)
- fopen("/dev/null", "r"): opens the null file, which is always empty; allocates the lowest unopened file descriptor (will be 0/stdin after fclose(stdin))
- kill(pid,0): returns 0 if the process is still alive
- waitpid(pid, &status, WNOHANG): checks the exit code of pid (assuming pid has terminated). If it's a zombie, it returns pid and reaps the zombie. If it is still running (hasn't terminated), it returns 0 without waiting. If it is an invalid pid (e.g. has already been reaped), it returns -1.
- chdir(path): change the current working directory