`

作业控制与孤儿进程组

阅读更多
    作业控制允许在一个终端上启动多个作业(进程组),它控制哪一个作业可以访问该终端以及哪些作业在后台运行。一个作业只是几个几个进程的集合,通常是一个进程管道。当启动一个后台作业时,shell 会赋予它一个作业标识符,并打印一个或多个进程 ID。如下。
$ make all > Make.out &
[1]    1475                # 作业编号是 1,所启动的进程 ID 是 1475
$ pr *.c | lpr &
[2]    1490                # 作业编号是 2,其第一个进程的进程 ID 是 1490

    只有前台作业可以接收终端输入,若后台作业试图读终端,则终端驱动程序会向后台作业发送一个 SIGTTIN 信号,它通常会停止此后台作业,而 shell 则向有关用户发出通知,然后用户就可以用 shell 命令将此作业转为前台作业运行,于是它就可以读终端了(不过在没有作业控制时,其处理方法是:如果该进程自己没有重定向标准输入,则 shell 自动将后台进程的标准输入重定向到 /dev/null。读 /dev/null 将产生一个文件结束,这意味着该后台进程会立即终止)。如下过程所示。
$ cat > temp.foo &         # 在后台启动,但将从标准输入读
[1]    1681
$
[1] + Stopped (SIGTTIN)    # SIGTTIN 信号停止后台作业
$ fg %1                    # 将该后台作业转变为前台作业,并发送继续信号(SIGCONT)
cat > temp.foo             # shell 告诉我们现在哪一个作业在前台
hello, world               # 输入一行
^D                         # 输入文件结束符
$ cat temp.foo             # 检查文件
hello, world

    而后台作业是否输出到控制终端是可以使用 stty 命令设置的(默认是允许的)。当禁止后台作业向控制终端写时,终端驱动程序就会向试图写其标准输出的后台作业发送 SIGTTOU 信号,它通常会阻塞该后台作业。如下过程所示。
$ cat temp.foo &           # 在后台执行
[1]    1719
$ hello, world             # 这是后台作业的输出

[1] + Done    cat temp.foo &
$ stty tostop              # 禁止后台作业输出到控制终端
$ cat temp.foo &           # 再执行一次
[1]    1721
$                          # 键入回车,发现后台作业已停止
[1] + Stopped(SIGTTOU)    cat temp.foo &
$ fg %1                    # 在前台恢复作业运行
cat temp.foo               # shell 告诉我们现在哪一个作业在前台
hello.world

    下图总结了作业控制的某些功能。图中穿过终端驱动程序框的实线表明终端 I/O 和终端产生的信号总是从前台进程组连接到实际终端,而对应于 SIGTTOU 信号的虚线表明后台进程组进程的输出是否出现在终端是可选择的。


    一个父进程已终止的进程称为孤儿进程,它会由 init 进程“收养”,而整个进程组也可成为“孤儿”。POSIX.1 将孤儿进程组定义为:该组中每个成员的父进程要么是该组的一个成员,要么不是该组所属会话的成员。即一个进程组不是孤儿进程组的条件是:该组中有一个进程,其父进程在属于同一会话的另一个组中。如果进程组不是孤儿进程组,那么在属于同一会话的另一个组中的父进程就有机会重新启动该组中停止的进程。
    考虑一个进程,它 fork 了一个子进程后就终止,但在它终止时,如果该子进程停止(用作业控制)又将如何?子进程如何继续,以及是否知道它已经是孤儿进程?
    下面这个程序演示了这种情形。该程序假定使用了一个作业控制 shell,shell 会将前台进程放在它们自己的进程组中,shell 则留在自己的进程组内。子进程继承其父进程的进程组。在 fork 后:
    * 父进程睡眠 5 秒,这是让子进程在父进程终止前运行的一种权宜之计。
    * 子进程为挂断信号建立信号处理程序,以观察 SIGHUP 信号是否已发送给子进程。
    * 子进程用 kill 函数向其自身发送停止信号 SIGTSTP(类似于使用 Ctrl+Z)。
    * 当父进程终止时,进程组包含一个停止的进程,该子进程成为孤儿进程,其父进程变成 init 进程,同时成为一个孤儿进程组的成员。POSIX.1 要求向新孤儿进程组中处于停止状态的每一个进程发送挂断信号(SIGHUP),接着又向其发送继续信号(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");  // Linux 中 read 后才变为后台进程组
	}
	exit(0);
}

    运行结果示例:
$ ./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  # 前台进程组变为了 82002
read error 5 on controlling TTY

    这里父进程终止后,子进程变成了后台进程组,此时子进程第二次调用 pr_ids 后,程序企图读标准输入,所以会对该后台进程组产生 SIGTTIN 信号。但因为这是一个孤儿进程组,如果内核用此信号停止它,则此进程组中的进程就再也不会继续。因此 POSIX.1 规定,这种情况下 read 返回出错,并将 errno 设置为 EIO(值一般为 5)。另外需要注意的是,在某些如 Linux 的实现中,子进程在父进程终止后依然是前台进程组,所以其后的 read 调用可以正常读取标准输入,不过它在 read 之后也会转变为后台进程组。
  • 大小: 20.2 KB
分享到:
评论

相关推荐

    Linux守护进程的研究.pdf

    1. 分离控制终端:通过fork两次,第一次创建子进程,第二次使子进程再次fork,让孙子进程成为孤儿进程,由init接管,从而脱离控制终端。 2. 改变工作目录:通常更改为根目录(/),防止占用启动目录。 3. 重定向标准...

    完整版系统进程管理模块代码.e.rar

    14. **僵尸进程和孤儿进程**:僵尸进程是已完成执行但其父进程尚未读取其退出状态的进程;孤儿进程是其父进程已经终止的进程,通常由init进程接管。 通过对这个压缩包中的代码进行学习和分析,我们可以深入了解这些...

    UNIX环境高级编程13

    2. 调用`setsid`创建新的会话和进程组,断开与控制终端的连接。 3. 更改工作目录至根目录(`/`),避免占用启动目录资源。 4. 关闭不必要的文件描述符,防止意外的数据交互。 函数`daemon_init`通常用于实现这些...

    mruby-process-pgrp:Process.getpgrp和Process.setpgid

    进程组在处理信号、控制终端等方面具有重要作用,特别是在实现作业控制和进程间通信时。 `Process.getpgrp` 函数在C语言中用于获取当前进程的进程组ID。这个ID对应于调用进程所在的进程组。在多进程环境中,知道...

    我的APUE2读书笔记

    当进程组的领导者死亡时,该组成为孤儿进程组。 #### 第十章 信号 **1. 基本概念** 信号是一种轻量级的进程间通信机制。 **2. 常用信号略述** 例如SIGINT、SIGTERM等。 **3. signal(2)函数** 用于捕获信号并指定...

    调度算法实验

    此外,还讨论了如何撤销进程、孤儿进程和僵尸进程的处理方法。 - **通过Shell命令创建进程**: - 使用`file`命令查看文件类型。 - 使用`ps`命令查看当前系统中的进程信息,了解进程的状态和所运行的终端。 - 使用...

    APUE读书笔记(Unix高级环境编程)

    - **孤儿进程组**:当进程组的领导者退出时,该组变为孤儿。 #### 第十章 信号 - **基本概念**:信号是一种异步通知机制。 - **常用信号**:如SIGINT、SIGKILL、SIGTERM等。 - **signal(2)函数**:用于设置信号...

    UNIX环境高级编程_第二版中文

    9.10 孤儿进程组  9.11 FreeBSD实现  9.12 小结  习题  第10章 信号  10.1 引言  10.2 信号概念  10.3 signal函数  10.4 不可靠的信号  10.5 中断的系统调用  10.6 可重入函数  10.7 SIGCLD...

    UNIX环境高级编程部分

    - **孤儿进程组**:没有父进程的进程组。 - **4.3+BSD实现**:特定实现的细节。 #### 第10章:信号 - **信号的概念**:操作系统向进程发送的通知。 - **signal函数**:设置信号处理器。 - **不可靠信号**:可能...

    UNIX环境高级编程(PDF)

    9.10 孤儿进程组 193 9.11 4.3+BSD实现 195 9.12 小结 197 习题 197 第10章 信号 198 10.1 引言 198 10.2 信号的概念 198 10.3 signal函数 203 10.3.1 程序起动 205 10.3.2 进程创建 206 10.4 不可靠的信号 206 10.5...

    IT行业中必备技能之UNIX最佳学习资料

    9.10 孤儿进程组 193 9.11 4.3+BSD实现 195 9.12 小结 197 习题 197 第10章 信号 198 10.1 引言 198 10.2 信号的概念 198 10.3 signal函数 203 10.3.1 程序起动 205 10.3.2 进程创建 206 10.4 不可靠的信号 206 10.5...

    Unix高级编程 pdf

    9.10 孤儿进程组 193 9.11 4.3+BSD实现 195 9.12 小结 197 习题 197 第10章 信号 198 10.1 引言 198 10.2 信号的概念 198 10.3 signal函数 203 10.3.1 程序起动 205 10.3.2 进程创建 206 10.4 不可靠的...

    UNIX环境高级编程和源代码

    9.10 孤儿进程组 193 9.11 4.3+BSD实现 195 9.12 小结 197 习题 197 第10章 信号 198 10.1 引言 198 10.2 信号的概念 198 10.3 signal函数 203 10.3.1 程序起动 205 10.3.2 进程创建 206 10.4 不可靠的信号 206 10.5...

    UNIX高级编程 计算机科学丛书

    9.10 孤儿进程组 9.11 4.3+BSD实现 9.12 小结 习题 第10章 信号 10.1 引言 10.2 信号的概念 10.3 signal函数 10.3.1 程序起动 10.3.2 进程创建 10.4 不可靠的信号 10.5 中断的系统调用 10.6 可再入函数 10.7 ...

    UNIX环境高级编程(中文版+英文版+源代码)

    9.10 孤儿进程组 193 9.11 4.3+BSD实现 195 9.12 小结 197 习题 197 第10章 信号 198 10.1 引言 198 10.2 信号的概念 198 10.3 signal函数 203 10.3.1 程序起动 205 10.3.2 进程创建 206 10.4 不可靠的信号 206 10.5...

Global site tag (gtag.js) - Google Analytics