|
Requires
- Some experience of Unix
- Good knowledge of 'C' Programming
Contents
A process is basically a single running program.
It may be a ``system'' program (e.g login, update, csh) or
program initiated by the user (textedit, dbxtool or a user written one).
When UNIX runs a process it gives each process a unique number - a process ID, pid.
The UNIX command ps will list all current processes running on your machine and will list the pid.
The C function int getpid() will return the pid of process that called this function.
A program usually runs as a single process. However later we will see how we can make programs run as several separate communicating processes.
We can run commands from a C program just as if they were from the UNIX
command line by using the system() function. NOTE: this can save
us a lot of time and hassle as we can run other (proven) programs, scripts
etc. to do set tasks.
int system(char *string) -- where string can be
the name of a unix utility, an executable shell script or a user program. System
returns the exit status of the shell. System is prototyped in
<stdlib.h>
Example: Call ls from a program
main()
{
printf(``Files in Directory are:n'');
system(``ls -l'');
}
system is a call that is made up of 3 other system calls: execl(),
wait() and fork() (which are prototyed in <unistd>)
execl has 5 other related functions -- see man pages.
execl stands for execute and leave which means
that a process will get executed and then terminated by execl.
It is defined by:
execl(char *path, char *arg0,...,char *argn, 0);
The last parameter must always be 0. It is a NULL terminator. Since
the argument list is variable we must have some way of telling C when it is to
end. The NULL terminator does this job.
where path points to the name of a file holding a command that is to
be executed, argo points to a string that is the same as path (or at
least its last component.
arg1 ... argn are pointers to arguments for the command and 0 simply
marks the end of the (variable) list of arguments.
So our above example could look like this also:
main()
{
printf(``Files in Directory are:n'');
execl(`/bin/ls'',``ls'', ``-l'',0);
}
int fork() turns a single process into 2 identical processes, known
as the parent and the child. On success, fork() returns 0 to
the child process and returns the process ID of the child process to the parent
process. On failure, fork() returns -1 to the parent process, sets errno to
indicate the error, and no child process is created.
NOTE: The child process will have its own unique PID.
The following program illustrates a simple use of fork, where two copies are
made and run together (multitasking)
main()
{ int return_value;
printf(``Forking processn'');
fork();
printf(``The process id is %d
and return value is %dn",
getpid(), return_value);
execl(``/bin/ls/'',``ls'',``-l'',0);
printf(``This line is not printedn'');
}
The Output of this would be:
Forking process
The process id is 6753 and return value is 0
The process id is 6754 and return value is 0
two lists of files in current directory
NOTE: The processes have unique ID's which will be different at each
run.
It also impossible to tell in advance which process will get to CPU's time --
so one run may differ from the next.
When we spawn 2 processes we can easily detect (in each process) whether it
is the child or parent since fork returns 0 to the child. We can
trap any errors if fork returns a -1. i.e.:
int pid; /* process identifier */
pid = fork();
if ( pid < 0 )
{ printf(``Cannot fork!!n'');
exit(1);
}
if ( pid == 0 )
{ /* Child process */ ...... }
else
{ /* Parent process pid is child's pid */
.... }
int wait (int *status_location) -- will force a parent process to
wait for a child process to stop or terminate. wait() return the pid of
the child or -1 for an error. The exit status of the child is returned to
status_location.
void exit(int status) -- terminates the process which calls this
function and returns the exit status value. Both UNIX and C (forked)
programs can read the status value.
By convention, a status of 0 means normal termination any other
value indicates an error or unusual occurrence. Many standard library calls have
errors defined in the sys/stat.h header file. We can easily derive our
own conventions.
A complete example of forking program is originally titled fork.c:
/* fork.c - example of a fork in a program */
/* The program asks for UNIX commands to be typed and inputted to a string*/
/* The string is then "parsed" by locating blanks etc. */
/* Each command and sorresponding arguments are put in a args array */
/* execvp is called to execute these commands in child process */
/* spawned by fork() */
/* cc -o fork fork.c */
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
main()
{
char buf[1024];
char *args[64];
for (;;) {
/*
* Prompt for and read a command.
*/
printf("Command: ");
if (gets(buf) == NULL) {
printf("\n");
exit(0);
}
/*
* Split the string into arguments.
*/
parse(buf, args);
/*
* Execute the command.
*/
execute(args);
}
}
/*
* parse--split the command in buf into
* individual arguments.
*/
parse(buf, args)
char *buf;
char **args;
{
while (*buf != NULL) {
/*
* Strip whitespace. Use nulls, so
* that the previous argument is terminated
* automatically.
*/
while ((*buf == ' ') || (*buf == '\t'))
*buf++ = NULL;
/*
* Save the argument.
*/
*args++ = buf;
/*
* Skip over the argument.
*/
while ((*buf != NULL) && (*buf != ' ') && (*buf != '\t'))
buf++;
}
*args = NULL;
}
/*
* execute--spawn a child process and execute
* the program.
*/
execute(args)
char **args;
{
int pid, status;
/*
* Get a child process.
*/
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
/* NOTE: perror() produces a short error message on the standard
error describing the last error encountered during a call to
a system or library function.
*/
}
/*
* The child executes the code inside the if.
*/
if (pid == 0) {
execvp(*args, args);
perror(*args);
exit(1);
/* NOTE: The execv() vnd execvp versions of execl() are useful when the
number of arguments is unknown in advance;
The arguments to execv() and execvp() are the name
of the file to be executed and a vector of strings contain-
ing the arguments. The last argument string must be fol-
lowed by a 0 pointer.
execlp() and execvp() are called with the same arguments as
execl() and execv(), but duplicate the shell's actions in
searching for an executable file in a list of directories.
The directory list is obtained from the environment.
*/
}
/*
* The parent executes the wait.
*/
while (wait(&status) != pid)
/* empty */ ;
}
Exercise 12727
Use popen() to pipe the rwho (UNIX command) output into
more (UNIX command) in a C program.