Processes are the things that actually do the work.
Program
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.
Process
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
create a process
destroy a process
run a process
suspend a process
get process information
set process information
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.
Synchronous
If a process is created from another as a synchronous process then the new
must complete execution before the old one can resume.
Asynchronous
If the new process is created asynchronously, then the two processes may be
run in pseudo-parallel.
Parent
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.
Granularity
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
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.
Example:
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.
or
fork
The function call to do this is
#include
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.
exec
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.
Example:
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
wait
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
#include
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 */
}
sleep
A process may suspend for a period of time using the sleep command
unsigned int sleep(seconds)
Process removal
exit
When a process executes the ``exit'' command it terminates.
kill
One process can send simple messages to another using the ``kill'' command
#include
#include
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);
}
signal
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: