Teaching‎ > ‎CS 4520: Operating Systems‎ > ‎Projects‎ > ‎

Shell 2

So far, your shell just runs external programs.  Try the following interesting programs from your shell:
  • ls
  • pwd
  • ps
  • kill
  • echo
  • cat
  • 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
For example:

mysh$ sleep 1000 &
mysh: started job 1209
mysh$ jobs
mysh$ kill 1209
mysh: job 1209 exited with status 0
mysh$ jobs
mysh$ cd /new/working/directory
mysh$ pwd
mysh$ ls
file1    file2    file3
mysh$ exit

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()

Function Reference

  • 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