The only way to create new processes is to fork() them. Here are some rules about forking and killing processes that you should remember:
The moral is this: in general, wait() for each process that you fork(), or have them reparented to init. Otherwise, your process table will fill with zombies and you won't be able to fork() anything.
This call returns twice: once to the parent, and once to the child process. The program can tell which one it is by checking the return value of the call: fork() returns 0 to the child process, and returns the PID of the child to the parent:
. . if ((pid = fork()) == 0) { printf("I'm the child and my parent is PID %d!\n", getppid()); exit(0); } printf("I'm the parent and my child is PID %d!\n", pid); wait(NULL); . .
As you can see, the child can always get its parent's PID from the call getppid().
When the child is fork()ed, it inherits many things from the parent (adapted from Stevens APITUE):
Things that the child does not share with the parent include:
Also from the previous example, you can see that a call to exit() will end a (child) process. Well, all processes are children at some level or another (except for a select couple). You can also call the more direct _exit() or abort() (raises SIGABRT) or wait for one of a dozen other signals that will terminate you.
For controlled termination, exit() will work for almost all occasions.
The status that you pass it? It can be retrieved through a wait() call.
Both of these calls are similar--wait() waits for the next child process to be reaped; waitpid() waits for a specific child process and allows you to specify some additional options.
With both functions, if status is not NULL, the child processes encoded return value is stored in it. Several macros exist to help figure this value out (these take the status itself, not a pointer to it!):
| ||||||
| ||||||
|
Again, you can just set this pointer to NULL if you don't care what the exit status is.
The second call, waitpid(), allows you to specify which PID to wait for. This has a little more functionality than first appears. The pid can be one of:
Finally, there is an options argument to waitpid(). This can be assigned one or both of the following value by ORing them together (or set options to 0 if you don't want any of them):
The WNOHANG option leaves room for abuse; people might think it's a good idea to poll for zombie children, but this can gobble CPU time. It's better to handle the signal SIGCHLD which is raised whenever a child process dies. The handler can then reap the zombie asynchronously.
There are two variants of exec: execl and execv. The prototypes are:
int execl(const char *path, const char *arg, ...); int execv(const char *path, char *const arg[]);
Each execl() takes a list of arguments to the program, ending with a NULL. The first argument should be the name of the program, itself:
execl("/bin/ls", "ls", "-l", NULL);
Whereas execv() takes an array similar to argv[]:
char *arg[] = { "ls", "-l", NULL }; execv("/bin/ls", arg);
To convolute matters, both execl() and execv() have three sub-variants (you've already seen the first of these). The other two add a "p" or "e" suffix to the call:
int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg , ..., char *const envp[]); int execvp(const char *file, char *const argv[]); int execve(const char *filename, const char *argv [], const char *envp[]);
A suffix of "p" means, search the PATH environment variable for this command. For instance, if we have "/bin" in the PATH, we can use execlp() to run ls:
execlp("ls", "ls", "-l", NULL);
Lastly, the "e" suffix tells exec that you want to pass an array of environment variables to be used by the new process instead of that of the currently running process. First declare an argv-style array with the environment variables you want to pass, then include them in the argument list to one of the exec-e functions:
char *env[] = { "PATH=/bin:/usr/bin", "HOME=/home/beej", NULL }; execle("/bin/ls", "ls", "-l", NULL, env);
As was previously mentioned, most often you will be fork()ing right before the exec.
This is a library routine that has a fork(), exec(), and waitpid() all bundled into one. It will fork off a child process, execute the named command, and wait for it to complete:
system("/bin/ls -l");
If the command has any output and you're using buffered I/O, you should fflush() the common output streams (like stdout and stderr) before calling system(), or the output might be out of order.
It is a C library routine, not a system call. Nevertheless, it can be very useful at times.
There are three kinds of user IDs:
There are corresponding versions of the group IDs.
These functions return your (numerical) UID and EUID, respectively. Use the getpwuid()-type functions to retrieve other user information.
These correspond to the above calls for UIDs, except they operate on GIDs.
If you're root, a call to setuid() will set all three of your UID, EUID, and saved set-UID. If you're a normal user, you can use this call to set your EUID to either your real UID or your saved set-UID. That's all.
The call seteuid() will only set your EUID, regardless of whether you are root or not.
These correspond to the above calls for UIDs, except they operate on GIDs.
Before saved set-UIDs, you could make these calls in 4.3BSD to set your UID and EUID. If you are root, you can set them both to whatever you want. Otherwise, you are only allowed to set UID to EUID and vice versa. If saved set-UIDs are supported by your system, you are also allowed to set UID to saved set-UID.
This call turns on process accounting and dumps records to the specified file name. Each time a process terminates, information will be appended to this file. If filename is NULL, accounting is turned off.
The information stored in each record of the binary file is defined as
struct acct { char ac_comm[ACCT_COMM]; /* Accounting command name */ time_t ac_utime; /* Accounting user time */ time_t ac_stime; /* Accounting system time */ time_t ac_etime; /* Accounting elapsed time */ time_t ac_btime; /* Beginning time */ uid_t ac_uid; /* Accounting user ID */ gid_t ac_gid; /* Accounting group ID */ dev_t ac_tty; /* controlling tty */ char ac_flag; /* Accounting flag */ long ac_minflt; /* Accounting minor pagefaults */ long ac_majflt; /* Accounting major pagefaults */ long ac_exitcode; /* Accounting process exitcode */ };
The field ac_flag can be any of the following OR'd together (might not hold for all systems--check your man page):
Of course, you have to be superuser to turn process accounting on and off. If accounting is already on and you want to examine the file (and it's readable), it's generally /var/adm/pacct.
Returns the name of the user logged in to the controlling terminal of this process.
This function returns the number of clock ticks since the system was last booted, and fills the buf structure with information pertaining to the number of clock ticks that have elapsed for the current process (and its child processes).
The argument buf is a pointer to a
struct tms { clock_t tms_utime; /* user time */ clock_t tms_stime; /* system time */ clock_t tms_cutime; /* user time of children */ clock_t tms_cstime; /* system time of children */ };
By sampling the time before a section of code (or child process) is executed, then sampling it again afterward and subtracting, you can come up with the total real time, user time, and system time used by that section of a process.
Actually, it returns the number of clock ticks for real, user, and system time, but you can find the number of seconds by dividing it by CLK_TCK (the number of ticks per second). CLK_TCK