Suppose pid X
is a process group leader and X
terminates, but other processes in the process group remain running (with X
as their pgid). Will Linux prevent the value X
from being assigned as a pid to a new process?
I ask this because of a failure condition POSIX allows for setsid
:
[EPERM] The calling process is already a process group leader, or the process group ID of a process other than the calling process matches the process ID of the calling process.
This error seems to be an unrecoverable condition for code using process groups (i.e. shells) that would be triggered "at random", making it even more odious. I would assume any implementation aiming at sane levels of quality would avoid reassigning X
as a pid while it's still in use as a pgid, but I can't find this documented anywhere.
Process IDs, in the first place, are usually allocated on a sequential basis, beginning at 0 and rising to a maximum value which varies from system to system. Once this limit is reached, allocation restarts at 300 and again increases. In macOS and HP-UX, allocation restarts at 100.
By convention, the process group ID of a process group equals the process ID of the first member of the process group, called the process group leader. A process finds the ID of its process group using the system call getpgrp() , or, equivalently, getpgid(0) .
PIDs are normally assigned sequentially until they hit some maximum value and roll over.
setpgid() sets the process group ID of the process specified by pid to pgid. If pid is zero, the process ID of the current process is used. If pgid is zero, the process ID of the process specified by pid is used.
Not a problem, because fork guarantees:
The child process ID also shall not match any active process group ID.
And fork
is the only way to create new processes.
Nemo is correct that POSIX guarantees that fork()
will not re-use an existing PGID as a PID; however, there is more to the story.
Process groups, and process group leaders, can also be changed using setpgid()
. The following example code causes the existence of a process group equal to the PID of the current process, which the current process is not in:
#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
int main()
{
pid_t pgrp_orig;
pid_t child;
int status;
/* Fork so that we are not a process group leader */
if (fork()) {
/* Grandparent process */
wait(&status);
return 0;
}
/* Record our original process group, then start a new one */
pgrp_orig = getpgrp();
if (setpgid(0, 0))
perror("setpgid");
child = fork();
if (!child) {
/* Child process */
pause();
return 0;
}
/* Switch back to original process group. Child remains in the new one */
if (setpgid(0, pgrp_orig))
perror("setpgid");
printf("Parent pid=%ld, pgid=%ld\n", (long)getpid(), (long)getpgrp());
printf("Child pid=%ld, pgid=%ld\n", (long)child, (long)getpgid(child));
/* Wake child up to finish up */
kill(child, SIGUSR1);
wait(&status);
return 0;
}
Note that if the parent process tries to call setsid()
here before the child exits, the failure condition you asked about will be triggered.
However, due to the restrictions on the allowable transitions that setpgid()
can cause, this can't cause the kind of random failures you're worried about. The breakage is confined to a single session.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With