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.