Processes are the things that actually do the work.


A program is a sequence of instructions. A program may be C source code. After compilation there is a program which is executable code. Neither of these actually do anything.


A process is a dynamic invocation of a program along with the resources required to run. These include user and system stacks, memory, file handles, etc. Each process generally acts in a separate address space.

Concurrent processes

A process is usually sequential and consists of a sequence of actions that take place one at a time. When a set of processes are run on a single CPU, using timeslicing to multitask them, this is referred to as concurrent sequential processing or pseudo-parallel processing. If there is more than one CPU available, then processes may be run in parallel.

Process Operations

The set of operations on processes includes The O/S will supply mechanisms for at least some of these.

Process creation

There must be a mechanism for creating processes by the O/S. A new process will be created using an existing one. There are several possible organisations of this.


If a process is created from another as a synchronous process then the new must complete execution before the old one can resume.


If the new process is created asynchronously, then the two processes may be run in pseudo-parallel.


When a new process is created, it may use the old one as ``parent''. This is done in MSDOS and Unix. Alternatively, there may be no relation between the original and the new process, as in Windows NT.


Processes may be complicated to set up and there may be very few of them. These are often called tasks. Alternatively there may be lots of processes and it may be easy to create them. On the Suns in the lab, upto 96 processes may be run and it is reasonably easy to create them. Very ``lightweight'' processes are now called threads, and there may be many threads in one address space.

Standard C API

The standard C library has a call #include <stdlib.h> int system(char *command) This creates a new synchronous process. It does so by passing the command to the command processor (eg zsh) which executes in the current command processor environment. When the process completes the system call finishes and the calling process can continue.


From a lab: read lines from standard input and execute them as commands.

The called process may in turn make a call to ``system''. This allows a stack of processes to be built, each of which is suspended when a new process is placed on the stack, and can resume when it becomes the top-of-stack process again. This mechanism is available in both MSDOS and Unix.

Unix API

The Unix API is more complex. It allows creation of asynchronous processes where each process has a parent. Each process has a process ID (a number) that can be used to tell them apart. This PID is shown in the user-level command ``ps''. Because the parent process can resume execution before a child terminates, the parent can continue to create processes. The children can also create processes

This creates a tree of processes.



The function call to do this is #include <sys/types.h> pid_t fork(void) This splits the current process into two almost identical copies. A copy is made of the user and system stacks, of the allocated data space and of the registers. The major difference is that one has the PID of the parent, the other has a new PID. The fork returns a value that is a PID. The PID is zero if you are the child, or the PID of the child if you are the parent.
Example: This creates one child:

What order are these two messages printed in? The child and the parent can call any functions in the program, and execute as independent processes with the same program code. There is (at present) no communication between them.


It is common to want to replace one of these processes so that it uses a different program. For example, suppose the process creates a file that it wants printed. In Unix it cannot print it itself, because it does not have access to the line printer device. The program ``lpr'' must be used. One way is system("lpr myfile"); This runs synchronously, so until the line printer program terminates the calling program suspends. Printing an empty file takes 150 milliseconds on my Sun. A preferable way may be to fork a new process and let the parent continue with the main piece of work, while the child prints the file asynchronously. This is possible, but cruddy: if (fork() == 0) system("lpr myfile"); else { /* parent */ } It creates a child process that does nothing but wait for ``system'' to finish. Better is to replace the child with the ``lpr'' program. This is the job of the ``exec'' functions int execlp(char *file, char *arg0, char *arg1, .... char *argn, (char *) 0); The ``file'' argument is the name of the program to execute. This uses the PATH environment variable to find the file (like shell commands do). ``arg0'' is often the same as ``file''. This is used as the name of the process, as reported by ``ps''. ``arg1'' to ``argn'' are the arguments to the command. This must be terminated by a NULL pointer to show the end of the list. In normal operation ``exec'' never returns. If if fails, it returns -1.


Fork and use exec to print a file if (fork() == 0) if (execlp("lpr", "lpr", "myfile", (char *) 0) == -1) fprintf(stderr, "exec failed\n"); else { /* parent */ } or more simply if (fork() == 0) { execlp("lpr", "lpr", "myfile", (char *) 0); fprintf(stderr, "exec failed\n"); } else { /* parent */ } Here is a simple example:

(What happens if the exit(2) is removed and the execlp fails? Why?)

Process suspension


Suppose the file was a temporary file. After printing, it should be deleted. The child cannot delete it, because after the ``exec'' it no longer exists. The parent cannot directly delete it because the ``lpr'' program may still be accessing it. (Actually, an option on lpr allows removal after printing - ignore this.) When ``lpr'' terminates, the parent can remove the file. So the parent has to be able to decide when a child has finished. #include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status) This waits for a process to terminate and returns the PID of one that did. It also returns status information. if ((pid = fork()) == 0) if (execlp("lpr", "lpr", "myfile", (char *) 0) == -1) fprintf(stderr, "exec failed\n"); else { /* do lots of things while the printing takes place */ /* now remove the file */ while (wait(NULL) != pid) ; unlink("myfile"); /* and do lots more things */ }


A process may suspend for a period of time using the sleep command unsigned int sleep(seconds)

Process removal


When a process executes the ``exit'' command it terminates.


One process can send simple messages to another using the ``kill'' command #include <sys/types.h> #include <signal.h> int kill( pid_t pid, int sig); The process to which the signal is sent must have the same uid or be a ``super user'' process. The signal is often SIGINT or SIGKILL. if (pid = (fork() == 0)) ... else { /* let the child run for 10 seconds max */ sleep(10); kill(pid, SIGINT); }


A process can catch certain signals by installing a signal handler which is a function invoked when the signal arrives.

Process environment

A process has an entry in a table of processes. This table contains all sorts of information (see next lecture) including user ID, current directory, etc. A set of function calls allows access to much of this information:

Process ID

#include <sys/types.h> pid_t getpid(void);

User ID

#include <sys/types.h> uid_t getuid(void);

User name

char *getlogin(void);

Current directory

char *getcwd(char *dir, int size); Example: Who is running this program?

This page is, copyright Jan Newmarch.
It is maintained by Jan Newmarch.
Last modified: 29 August, 1995