Home > Articles > Operating Systems, Server > Linux/UNIX/Open Source

  • Print
  • + Share This
This chapter is from the book

10.4 Process Primitives

Despite the relatively long discussion needed to describe a process, creating and destroying processes in Linux is straightforward.

10.4.1 Having Children

Linux has two system calls that create new processes: fork() and clone(). As mentioned earlier, clone() is used for creating threads and is discussed briefly later in this chapter. For now, we focus on fork(), which is the most popular method of process creation.

#include <unistd.h>

pid_t fork(void);

This system call has the unusual property of not returning once per invocation, but twice: once in the parent and once in the child. Note that we did not say "first in the parent"—writing code that makes any assumptions about the two processes executing in a deterministic order is a very bad idea.

Each return from the fork() system call returns a different value. In the parent process, the system call returns the pid of the newly created child process; in the child process, the call returns 0.

The difference in return value is the only difference apparent to the processes. Both have the same memory image, credentials, open files, [4] and signal handlers. Here is a simple example of a program that creates a child:

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>

int main(void) {
    pid_t child;

    if (!(child = fork())) {
        printf("in child\n");
        exit(0);
    }
    printf("in parent -- child is %d\n", child);
    return 0;
}

10.4.2 Watching Your Children Die

Collecting the exit status of a child is called waiting on the process. There are four ways this can be done, although only one of the calls is provided by the kernel. The other three methods are implemented in the standard C library. As the kernel system call takes four arguments, it is called wait4().

pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage);

The first argument, pid, is the process whose exit code should be returned. It can take on a number of special values.

< -1

Waits for any child whose pgid is the same as the absolute value of pid.

= -1

Waits for any child to terminate.

= 0

Waits for a child in the same process group current process. [5]

> 0

Waits for process pid to exit.

The second parameter is a pointer to an integer that gets set to the exit status of the process that caused the wait4() to return (which we hereafter call the examined process). The format of the returned status is convoluted, and a set of macros is provided to make sense of it.

Three events cause wait4() to return the status of the examined process: The process could have exited, it could have been terminated by a kill() (sent a fatal signal), or it could have been stopped for some reason. [6] You can find out which of these occurred through the following macros, each of which takes the returned status from wait4() as the sole parameter:

WIFEXITED(status)

Returns true if the process exited normally. A process exits normally when its main() function returns or the program calls exit(). If WIFEXITED() is true, WEXITSTATUS(status) returns the process's exit code.

WIFSIGNALED(status)

Returns true if the process was terminated due to a signal (this is what happens for processes terminated with kill()). If this is the case, WTERMSIG(status) returns the signal number that terminated the process.

WIFSTOPPED(status)

If the process has been stopped by a signal, WIFSTOPPED() returns true and WSTOPSIG(status) returns the signal that stopped the process. wait4() returns information on stopped processes only if WUNTRACED was specified as an option.

The options argument controls how the call behaves. WNOHANG causes the call to return immediately. If no processes are ready to report their status, the call returns 0 rather than a valid pid. WUNTRACED causes wait4() to return if an appropriate child has been stopped. See Chapter 15 for more information on stopped processes. Both of these behaviors may be specified by bitwise OR'ing the two values together.

The final parameter to wait4(), a pointer to a struct rusage, gets filled in with the resource usage of the examined process and all the examined process's children. See the discussion of getrusage() and RUSAGE_BOTH on pages 118-119 for more information on what this entails. If this parameter is NULL, no status information is returned.

There are three other interfaces to wait4(), all of which provide subsets of its functionality. Here is a summary of the alternative interfaces.

pid_t wait(int *status)

The only parameter to wait() is a pointer to the location to store the terminated process's return code. This function always blocks until a child has terminated.

pid_t waitpid(pid_t pid, int *status, int options)

The waitpid() function is similar to wait4(); the only difference is that it does not return resource usage information on the terminated process.

pid_t wait3(int *status, int options, struct rusage *rusage)

This function is also similar to wait4(), but it does not allow the caller to specify which child should be checked.

10.4.3 Running New Programs

Although there are six ways to run one program from another, they all do about the same thing—replace the currently running program with another. Note the word replace —all traces of the currently running program disappear. If you want to have the original program stick around, you must create a new process with fork() and then execute the new program in the child process.

The six functions feature only slight differences in the interface. Only one of these functions, execve(), is actually a system call under Linux. The rest of the functions are implemented in user-space libraries and utilize execve() to execute the new program. Here are the prototypes of the exec() family of functions:

int execl(const char * path, const char * arg0, ...);
int execlp(const char * file, const char * arg0, ...);
int execle(const char * path, const char * arg0, ...);
int execv(const char * path, const char ** argv);
int execvp(const char * file, const char ** argv);
int execve(const char * file, const char ** argv, const char ** envp);

As mentioned, all of these programs try to replace the current program with a new program. If they succeed, they never return (as the program that called them is no longer running). If they fail, they return -1 and the error code is stored in errno, as with any other system call. When a new program is run (or exec() ed) it gets passed an array of arguments (argv) and an array of environment variables (envp). Each element in envp is of the form VARIABLE=value . [7]

The primary difference between the various exec() functions is how the command line arguments are passed to the new program. The execl family passes each element in argv (the command-line arguments) as a separate argument to the function, and NULL terminates the entire list. Traditionally, the first element in argv is the command used to invoke the new program. For example, the shell command /bin/cat /etc/passwd/etc/group normally results in the following exec call:

execl("/bin/cat", "/bin/cat", "/etc/passwd", "/etc/group", NULL);

The first argument is the full path to the program being executed and the rest of the arguments get passed to the program as argv. The final parameter to execl() must be NULL—it indicates the end of the parameter list. If you omit the NULL, the function call is likely to result in either a segmentation fault or return EINVAL. The environment passed to the new program is whatever is pointed to by the environ global variable, as mentioned on page 116.

The execv functions pass the command-line argument as a C array of strings, [8] which is the same format used to pass argv to the new program. The final entry in the argv array must be NULL to indicate the end of the array, and the first element (argv[0]) should contain the name of the program that was invoked. Our ./cat /etc/passwd /etc/group example would be coded using execv like this:

char * argv[] = { "./cat", "/etc/passwd", "/etc/group", NULL };
execv("/bin/cat", argv);

If you need to pass a specific environment to the new program, execle() and execve() are available. They are exactly like execl() and execv() but they take a pointer to the environment as their final argument. The environment is set up just like argv.

For example, here is one way to execute /usr/bin/env (which prints out the environment it was passed) with a small environment:

char * newenv[] = { "PATH=/bin:/usr/bin",
                    "HOME=/home/sweethome", NULL };
execle("/usr/bin/env", "/usr/bin/env", NULL, newenv);

Here is the same idea implemented with execve():

char * argv[] = { "/usr/bin/env", NULL };
char * newenv[] = { "PATH=/bin:/usr/bin",
                    "HOME=/home/sweethome", NULL };
execve("/usr/bin/env", argv, newenv);

The final two functions, execlp() and execvp(), differ from the first two by searching the current path (set by the PATH environment variable) for the program to execute. The arguments to the program are not modified, however, so argv[0] does not contain the full path to the program being run. Here are modified versions of our first example that search for cat in the current PATH:

execlp("cat", "cat", "/etc/passwd", "/etc/group", NULL);

char * argv[] = { "cat", "/etc/passwd", "/etc/group", NULL };
execvp("cat", argv);

If execl() or execv() were used instead, those code fragments would fail unless cat was located in the current directory.

If you are trying to run a program with a specific environment while still searching the path, you need to search the path manually and use execle() or execve(), because none of the available exec() functions does quite what you want.

Signal handlers are preserved across the exec() functions in a slightly nonobvious way; the mechanism is described on page 205.

10.4.4 Faster Process Creation with vfork()

Normally processes that fork() immediately exec() another program (this is what shells do every time you type a command), making the full semantics of fork() more computationally expensive than is necessary. To help optimize this common case, vfork() is provided.

#include <unistd.h>

pid_t vfork(void);

Rather than creating an entirely new execution environment for the new process, vfork() creates a new process that shares the memory of the original process. The new process is expected to either _exit() or exec() another process very quickly, and the behavior is undefined if it modifies any memory, returns from the function the vfork() is contained in, or calls any new functions. In addition, the original process is suspended until the new one either terminates or calls an exec() function. [9] Not all systems provide the memory-sharing and parent-suspending semantics of vfork(), however, and applications should never rely upon that behavior.

10.4.5 Killing Yourself

Processes terminate themselves by calling either exit() or _exit(). When a process's main() function returns, the standard C library calls exit() with the value returned from main() as the parameter.

void exit(int exitCode)
void _exit(int exitCode)

The two forms, exit() and _exit(), differ in that exit() is a function in the C library, while _exit() is a system call. The _exit() system call terminates the program immediately, and the exitCode is stored as the exit code of the process. When exit() is used, functions registered by atexit() are called before the library calls _exit(exitCode). Among other things, this allows the ANSI/ISO standard I/O library to flush all its buffers.

Registering functions to be run when exit() is used is done through the atexit() function.

int atexit(void (*function)(void));

The only parameter passed to atexit() is a pointer to a function. When exit() is invoked, all the functions registered with atexit() are called in the opposite order from which they were registered. Note that if _exit() is used or the process is terminated due to a signal (see Chapter 12 for details on signals), functions registered via atexit() are not called.

10.4.6 Killing Others

Destroying other processes is almost as easy as creating a new one—just kill it:

int kill(pid_t pid, int signum);

pid should be the pid of the process to kill, and signum describes how to kill it. There are two choices [10] for how to kill a child. You can use SIGTERM to terminate the process gently. This means that the process can ask the kernel to tell it when someone is trying to kill it so that it can terminate gracefully (saving files, for example). The process may also ignore this type of request for it to terminate and, instead, continue running. Using SIGKILL for the signum parameter kills the process immediately, no questions asked. If signum is zero, then kill() checks to see if the process calling kill() has the proper permissions, and returns zero if so and nonzero if its permissions are insufficient. This provides a way for a process to check the validity of a pid.

The pid parameter can take on four types of values under Linux.

pid > 0

The signal is sent to the process whose pid is pid. If no process exists with that pid, ESRCH is returned.

pid < -1

The signal is sent to all the processes in the process group whose pgid is -pid. For example, kill(-5316, SIGKILL) immediately terminates all the processes in process group 5316. This ability is used by job control shells, as discussed in Chapter 15.

pid = 0

The signal is sent to all the processes in the current process's process group.

pid = -1

The signal is sent to all the processes on the system except the init process. This is used during system shutdown.

Processes can normally kill() only processes that share the same effective user ID as themselves. There are two exceptions to this rule. First, processes with an effective uid of 0 may kill() any process on the system. Second, any process can send a SIGCONT signal to any other process in the same session. [11]

10.4.7 Dumping Core

Although we just mentioned that passing SIGTERM and SIGKILL to kill() causes a process to terminate, you can use quite a few different values (Chapter 12 discusses all of them). Some of these, such as SIGABRT, cause the program to dump core before dying. A program's core dump contains a complete history of the state of the program when it died. [12] Most debuggers, including gdb, can analyze a core file and tell you what the program was doing when it died, as well as let you inspect the defunct process's memory image. Core dumps end up in the process's current working directory in a file called (simply enough) core.

When a process has violated some of the system's requirements (such as trying to access memory that it is not allowed to access), the kernel terminates the process by calling an internal version of kill() with a parameter that causes a core dump. The kernel may kill a process for several reasons, including arithmetic violations, such as division by zero; the programs running illegal instructions; and the programs trying to access inaccessible regions of memory. This last case causes a segmentation fault, which results in the message segmentation fault (core dumped). If you do any reasonable amount of Linux programming, you are sure to tire of this message!

If a process's resource limit for core files is 0 (see page 120 for details on the core resource limit), no core file is generated.

  • + Share This
  • 🔖 Save To Your Account

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020