Process abstraction and management

Required reading: Chapter 7 and remaining of chapter 12


The next set of lectures we will discuss the implementation of kernel services for process management, interprocess communication, I/O, and file systems. Today's topic is process management and the case study will be UNIX's process management services; all operating systems support something similar to what UNIX supports.

A process is one address space combined with one thread. Process management in UNIX include the functions fork, wait, exec, exit, kill, getpid, brk, nice, sleep, and trace. (All system calls in v6 are in the systent table on sheet 29; pick out the ones related to process management.)

To recall how they are used, remember the structure of the shell:

       while (1) {
	    printf ("$");
	    readcommand (command, args);
	    if ((pid = fork ()) == 0) {  // child?
	       exec (command, args, 0);   // arg 0 is name of program
	    } else if (pid > 0) {   // parent?
	       wait (pid);
	    } else {
	       printf ("Unable to fork\n");

As a side note, how does the shell get started? (Init fork+execs login, one per terminal; login execs the shell listed in the passwd file.)

How can a shell implement background jobs (a process group)?

        $ compute &
The shell just doesn't call wait, but instead reads the next command. The shell periodically polls jobs to find out what their status is by calling wait (passing a flag not to block). (Jobs are not supported by v6.)

A process terminates fully when (1) the process exited (perhaps because of kill); and (2) the parent has called wait. A process that has exited but the parent hasn't called wait, enters the zombie state. If a parent process terminates without waiting for all of its child processes to terminate, the remaining child processes are assigned the parent process 1 ID (the init process ID); init waits for processes to terminate, and thus will clean them up.

V6 code examples