Each process has a numeric process group ID that defines the process group to which it belongs. A new process inherits its parent’s process group ID. A process can obtain its process group ID using getpgrp().
#include <unistd.h>
pid_t getpgrp
(void)
Always successfully returns process group ID of calling process
If the value returned by getpgrp() matches the caller’s process ID, this process is the leader of its process group.
The setpgid() system call changes the process group of the process whose process ID is pid to the value specified in pgid.
#include <unistd.h>
int setpgid
(pid_t pid, pid_t pgid);
Returns 0 on success, or -1 on error
If pid is specified as 0, the calling process’s process group ID is changed. If pgid is specified as 0, then the process group ID of the process specified by pid is made the same as its process ID. Thus, the following setpgid() calls are equivalent:
setpgid(0, 0); setpgid(getpid(), 0); setpgid(getpid(), getpid());
If the pid and pgid arguments specify the same process (i.e., pgid is 0 or matches the process ID of the process specified by pid), then a new process group is created, and the specified process is made the leader of the new group (i.e., the process group ID of the process is made the same as its process ID). If the two arguments specify different values (i.e., pgid is not 0 and doesn’t match the process ID of the process specified by pid), then setpgid() is being used to move a process between process groups.
The typical callers of setpgid() (and setsid(), described in Sessions) are programs such as the shell and login(1). In Creating a Daemon, we’ll see that a program also calls setsid() as one of the steps on the way to becoming a daemon.
Several restrictions apply when calling setpgid():
The pid argument may specify only the calling process or one of its children. Violation of this rule results in the error ESRCH
.
When moving a process between groups, the calling process and the process specified by pid (which may be one and the same), as well as the target process group, must all be part of the same session. Violation of this rule results in the error EPERM
.
The pid argument may not specify a process that is a session leader. Violation of this rule results in the error EPERM
.
A process may not change the process group ID of one of its children after that child has performed an exec(). Violation of this rule results in the error EACCES
. The rationale for this constraint is that it could confuse a program if its process group ID were changed after it had commenced.
The restriction that a process may not change the process group ID of one of its children after that child has performed an exec() affects the programming of job-control shells, which have the following requirements:
All of the processes in a job (i.e., a command or a pipeline) must be placed in a single process group. (We can see the desired result by looking at the two process groups created by bash in Figure 34-1.) This step permits the shell to use killpg() (or, equivalently, kill() with a negative pid argument) to simultaneously send job-control signals to all of the members of the process group. Naturally, this step must be carried out before any job-control signals are sent.
Each of the child processes must be transferred to the process group before it execs a program, since the program itself is ignorant of manipulations of the process group ID.
For each process in the job, either the parent or the child could use setpgid() to change the process group ID of the child. However, because the scheduling of the parent and child is indeterminate after a fork() (Race Conditions After fork()), we can’t rely on the parent changing the child’s process group ID before the child does an exec(); nor can we rely on the child changing its process group ID before the parent tries to send any job-control signals to it. (Dependence on either one of these behaviors would result in a race condition.) Therefore, job-control shells are programmed so that the parent and the child process both call setpgid() to change the child’s process group ID to the same value immediately after a fork(), and the parent ignores any occurrence of the EACCES
error on the setpgid() call. In other words, in a job-control shell, we’ll find code something like that shown in Example 34-1.
Example 34-1. How a job-control shell sets the process group ID of a child process
pid_t childPid; pid_t pipelinePgid; /* PGID to which processes in a pipeline are to be assigned */ /* Other code */ childPid = fork(); switch (childPid) { case -1: /* fork() failed */ /* Handle error */ case 0: /* Child */ if (setpgid(0, pipelinePgid) == -1) /* Handle error */ /* Child carries on to exec the required program */ default: /* Parent (shell) */ if (setpgid(childPid, pipelinePgid) == -1 && errno != EACCES) /* Handle error */ /* Parent carries on to do other things */ }
Things are slightly more complex than shown in Example 34-1, since, when creating the processes for a pipeline, the parent shell records the process ID of the first process in the pipeline and uses this as the process group ID (pipelinePgid) for all of the processes in the group.
The different suffixes in the names of the getpgrp() and setpgid() system calls deserve explanation.
In the beginning, 4.2BSD provided a getprgp(pid) system call that returned the process group ID of the process specified by pid. In practice, pid was always used to specify the calling process. Consequently, the POSIX committee deemed the call to be more complex than necessary, and instead adopted the System V getpgrp() call, which took no arguments and returned the process group ID of the calling process.
In order to change the process group ID, 4.2BSD provided the call setpgrp(pid, pgid), which operated in a similar manner to setpgid(). The principal difference was that the BSD setpgrp() could be used to set the process group ID to any value. (We noted earlier that setpgid() can’t transfer a process into a process group in a different session.) This resulted in some security issues and was also more flexible than required for implementing job control. Consequently, the POSIX committee settled on a more restrictive function and gave it the name setpgid().
To further complicate matters, SUSv3 specifies getpgid(pid), with the same semantics as the old BSD getpgrp(), and also weakly specifies an alternative, System V-derived version of setpgrp(), taking no arguments, as being approximately equivalent to setpgid(0, 0).
Although the setpgid() and getpgrp() system calls that we described earlier are sufficient for implementing shell job control, Linux, like most other UNIX implementations, also provides getpgid(pid) and setpgrp(void). For backward compatibility, many BSD-derived implementations continue to provide setprgp(pid, pgid) as a synonym for setpgid(pid, pgid).
If we explicitly define the _BSD_SOURCE
feature test macro when compiling a program, then glibc provides the BSD-derived versions of setpgrp() and getpgrp(), instead of the default versions.