Job Control and Orphan Process Groups

    Job control allows multiple jobs (process groups) to be started on a terminal, it controls which job can access the terminal and which jobs run in the background. A job is just a collection of several processes, usually a process pipeline. When a background job is started, the shell assigns it a job identifier and prints one or more process IDs. as follows.
$ make all > Make.out &
[1] 1475 # The job number is 1, and the process ID started is 1475
$ pr *.c | lpr &
[2] 1490 # The job number is 2, and the process ID of its first process is 1490

    Only foreground jobs can receive terminal input, if a background job tries to read the terminal, the terminal driver sends a SIGTTIN signal to the background job, which usually stops the background job, and the shell notifies the concerned user, who can then use The shell command turns this job into a foreground job, so it can read the terminal (but in the absence of job control, the method is: if the process itself does not redirect standard input, the shell automatically converts the standard input of the background process Redirects to /dev/null. Reading /dev/null will produce an end-of-file, which means that the background process is terminated immediately). The process is shown below.
$ cat > temp.foo & # Starts in the background, but will read from stdin
[1]    1681
$
[1] + Stopped (SIGTTIN) # SIGTTIN signal stops background job
$ fg %1 # Turn the background job into a foreground job and send a continue signal (SIGCONT)
cat > temp.foo # shell tells us which job is currently in the foreground
hello, world # enter a line
^D # input end of file
$ cat temp.foo # check the file
hello, world

    Whether background jobs are output to the controlling terminal can be set using the stty command (allowed by default). When a background job is prohibited from writing to the controlling terminal, the terminal driver sends a SIGTTOU signal to a background job attempting to write to its standard output, which normally blocks the background job. The process is shown below.
$ cat temp.foo & # execute in background
[1]    1719
$ hello, world # This is the output of the background job

[1] + Done    cat temp.foo &
$ stty tostop # Disable background job output to the controlling terminal
$ cat temp.foo & # do it again
[1]    1721
$ # Type Enter and find that the background job has stopped
[1] + Stopped(SIGTTOU)    cat temp.foo &
$ fg %1 # Resume job running in foreground
cat temp.foo # shell tells us which job is currently in the foreground
hello.world

    The following figure summarizes some of the features of job control. The solid line across the terminal driver box in the figure indicates that terminal I/O and terminal-generated signals are always connected from the foreground process group to the actual terminal, while the dotted line corresponding to the SIGTTOU signal indicates whether the output of the background process group process appears on the terminal is optional.


    A process whose parent process has terminated is called an orphan process, it is "adopted" by the init process, and an entire process group can be "orphaned". POSIX.1 defines an orphan process group as: the parent process of each member of the group is either a member of the group or not a member of the session to which the group belongs. That is, a process group is not an orphan process group if there is a process in the group whose parent is in another group that belongs to the same session. If the process group is not an orphan process group, then a parent process in another group that belongs to the same session has the opportunity to restart a stopped process in that group.
    Consider a process that forks a child process and then terminates, but when it terminates, what if the child process is stopped (with job control)? How does the child process continue, and does it know that it is already an orphaned process?
    The following program demonstrates this situation. The program assumes that a job control shell is used, the shell will put foreground processes in their own process group, and the shell will stay in its own process group. A child process inherits the process group of its parent process. After fork:
    * The parent process sleeps for 5 seconds, which is a workaround to let the child process run until the parent process terminates.
    * The child process establishes a signal handler for the hangup signal to observe whether the SIGHUP signal has been sent to the child process.
    * The child process sends itself the stop signal SIGTSTP with the kill function (similar to using Ctrl+Z).
    * When the parent process terminates, the process group contains a stopped process, the child process becomes an orphan process, and its parent process becomes the init process and becomes a member of an orphan process group. POSIX.1 requires that every process in the new orphan process group that is in the stopped state be sent a hangup signal (SIGHUP) followed by a continue signal (SIGCONT).
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

static void sig_hup(int signo){
	printf("SIGHUP received, pid = %ld\n", (long)getpid());
}

static void pr_ids(char *name){
	printf("%s: pid = %ld, ppid = %ld, pgrp = %ld, tpgrp = %ld\n",
		name, (long)getpid(), (long)getppid(), (long)getpgrp(),
		(long)tcgetpgrp(STDIN_FILENO));
	fflush(stdout);
}

int main(void){
	char	c;
	pid_t	pid;

	pr_ids("parent");
	if((pid=fork()) < 0){
		printf("fork error\n");
	}else if(pid > 0){
		sleep(5);		// sleep to let child stop itself
	}else{
		pr_ids("child");
		signal (SIGHUP, sig_hup); // establish signal handler
		kill(getpid(), SIGTSTP);	// stop ourself
		pr_ids("child");		// prints only if we're continued
		if(read(STDIN_FILENO, &c, 1) != 1)
			printf("read error %d on controlling TTY\n", errno);
		// pr_ids("child"); // In Linux, it becomes a background process group after read
	}
	exit(0);
}

    Example of running result:
$ ./orphanPgrpDemo.out
parent: pid = 7083, ppid = 82002, pgrp = 7083, tpgrp = 7083
child: pid = 7084, ppid = 7083, pgrp = 7083, tpgrp = 7083
SIGHUP received, pid = 7084
child: pid = 7084, ppid = 1, pgrp = 7083, tpgrp = 82002 # The foreground process group has changed to 82002
read error 5 on controlling TTY

    Here, after the parent process is terminated, the child process becomes a background process group. At this time, after the child process calls pr_ids for the second time, the program attempts to read the standard input, so the background process group will generate a SIGTTIN signal. But because this is an orphan process group, if the kernel stops it with this signal, the processes in this process group will never continue. Therefore, POSIX.1 specifies that in this case read returns an error and sets errno to EIO (usually a value of 5). It should also be noted that in some implementations such as Linux, the child process is still the foreground process group after the parent process terminates, so the subsequent read calls can read the standard input normally, but it will also change to Background process group.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326118461&siteId=291194637