- 浏览: 143074 次
文章分类
最新评论
作业控制允许在一个终端上启动多个作业(进程组),它控制哪一个作业可以访问该终端以及哪些作业在后台运行。一个作业只是几个几个进程的集合,通常是一个进程管道。当启动一个后台作业时,shell 会赋予它一个作业标识符,并打印一个或多个进程 ID。如下。
只有前台作业可以接收终端输入,若后台作业试图读终端,则终端驱动程序会向后台作业发送一个 SIGTTIN 信号,它通常会停止此后台作业,而 shell 则向有关用户发出通知,然后用户就可以用 shell 命令将此作业转为前台作业运行,于是它就可以读终端了(不过在没有作业控制时,其处理方法是:如果该进程自己没有重定向标准输入,则 shell 自动将后台进程的标准输入重定向到 /dev/null。读 /dev/null 将产生一个文件结束,这意味着该后台进程会立即终止)。如下过程所示。
而后台作业是否输出到控制终端是可以使用 stty 命令设置的(默认是允许的)。当禁止后台作业向控制终端写时,终端驱动程序就会向试图写其标准输出的后台作业发送 SIGTTOU 信号,它通常会阻塞该后台作业。如下过程所示。
下图总结了作业控制的某些功能。图中穿过终端驱动程序框的实线表明终端 I/O 和终端产生的信号总是从前台进程组连接到实际终端,而对应于 SIGTTOU 信号的虚线表明后台进程组进程的输出是否出现在终端是可选择的。
一个父进程已终止的进程称为孤儿进程,它会由 init 进程“收养”,而整个进程组也可成为“孤儿”。POSIX.1 将孤儿进程组定义为:该组中每个成员的父进程要么是该组的一个成员,要么不是该组所属会话的成员。即一个进程组不是孤儿进程组的条件是:该组中有一个进程,其父进程在属于同一会话的另一个组中。如果进程组不是孤儿进程组,那么在属于同一会话的另一个组中的父进程就有机会重新启动该组中停止的进程。
考虑一个进程,它 fork 了一个子进程后就终止,但在它终止时,如果该子进程停止(用作业控制)又将如何?子进程如何继续,以及是否知道它已经是孤儿进程?
下面这个程序演示了这种情形。该程序假定使用了一个作业控制 shell,shell 会将前台进程放在它们自己的进程组中,shell 则留在自己的进程组内。子进程继承其父进程的进程组。在 fork 后:
* 父进程睡眠 5 秒,这是让子进程在父进程终止前运行的一种权宜之计。
* 子进程为挂断信号建立信号处理程序,以观察 SIGHUP 信号是否已发送给子进程。
* 子进程用 kill 函数向其自身发送停止信号 SIGTSTP(类似于使用 Ctrl+Z)。
* 当父进程终止时,进程组包含一个停止的进程,该子进程成为孤儿进程,其父进程变成 init 进程,同时成为一个孤儿进程组的成员。POSIX.1 要求向新孤儿进程组中处于停止状态的每一个进程发送挂断信号(SIGHUP),接着又向其发送继续信号(SIGCONT)。
运行结果示例:
这里父进程终止后,子进程变成了后台进程组,此时子进程第二次调用 pr_ids 后,程序企图读标准输入,所以会对该后台进程组产生 SIGTTIN 信号。但因为这是一个孤儿进程组,如果内核用此信号停止它,则此进程组中的进程就再也不会继续。因此 POSIX.1 规定,这种情况下 read 返回出错,并将 errno 设置为 EIO(值一般为 5)。另外需要注意的是,在某些如 Linux 的实现中,子进程在父进程终止后依然是前台进程组,所以其后的 read 调用可以正常读取标准输入,不过它在 read 之后也会转变为后台进程组。
$ 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 之后也会转变为后台进程组。
发表评论
-
打开伪终端设备
2018-07-09 20:50 1256在伪终端概述一节中已对 PTY进行了初步的介绍。尽管 ... -
伪终端概述
2018-06-02 11:05 1554伪终端就是指,一个应用程序看上去像一个终端,但事实上它 ... -
终端窗口大小和 termcap
2018-05-29 22:39 800多数 UNIX 系统都提供了一种跟踪当前终端窗口大小的 ... -
终端规范模式和非规范模式
2018-05-29 00:25 954终端规范模式很简单:发一个读请求,当一行已经输入后,终 ... -
终端标识
2018-05-23 11:18 571尽管控制终端的名字在多数 UNIX 系统上都是 /de ... -
波特率和行控制函数
2018-05-22 07:53 946虽然大多数终端设 ... -
终端属性和选项标志
2018-05-20 07:40 710tcgetattr 和 tcsetattr ... -
终端特殊输入字符
2018-05-17 06:33 818终端支持下表所示的特殊输入字符。 为了更改 ... -
终端 I/O 综述
2018-05-10 07:56 440终端设备可认为是由内核中的终端驱动程序控制的。每个终端 ... -
POSIX 信号量
2018-05-09 00:03 582在XSI IPC通信之信 ... -
XSI IPC 通信之共享存储
2018-04-25 07:18 948在XSI IPC通信之消息队列和XSI IPC通信之信 ... -
XSI IPC通信之信号量
2018-04-17 23:38 619在XSI IPC通信之消 ... -
XSI IPC通信之消息队列
2018-04-15 10:54 498消息队列是消息的链接表,存储在内核中,由消息队列标识符 ... -
XSI IPC 相似特征介绍
2018-02-08 23:48 487有 3 种称作 XSI IPC ... -
IPC 通信之 FIFO
2018-02-06 22:55 422FIFO 也被称为命名管道,未命名的管道只能在两个相关 ... -
IPC 通信之管道
2018-01-30 22:22 391管道是 UNIX 系统 IPC 的最古老但也是最常用的 ... -
readv/writev 函数及存储映射 I/O
2018-01-19 00:57 894readv 和 writev 函数可用于在一次函数调用 ... -
POSIX 异步 I/O
2018-01-16 21:33 456POSIX 异步 I/O 接口为对不同类型的文件进行异 ... -
fcntl 记录锁
2018-01-06 23:48 627记录锁的功能是:当有进程正在读或修改文件的某个部分时, ... -
守护进程惯例
2018-01-06 23:52 442UNIX 系统中,守护进程遵循下列通用惯例。 ...
相关推荐
1. 分离控制终端:通过fork两次,第一次创建子进程,第二次使子进程再次fork,让孙子进程成为孤儿进程,由init接管,从而脱离控制终端。 2. 改变工作目录:通常更改为根目录(/),防止占用启动目录。 3. 重定向标准...
14. **僵尸进程和孤儿进程**:僵尸进程是已完成执行但其父进程尚未读取其退出状态的进程;孤儿进程是其父进程已经终止的进程,通常由init进程接管。 通过对这个压缩包中的代码进行学习和分析,我们可以深入了解这些...
进程组在处理信号、控制终端等方面具有重要作用,特别是在实现作业控制和进程间通信时。 `Process.getpgrp` 函数在C语言中用于获取当前进程的进程组ID。这个ID对应于调用进程所在的进程组。在多进程环境中,知道...
当进程组的领导者死亡时,该组成为孤儿进程组。 #### 第十章 信号 **1. 基本概念** 信号是一种轻量级的进程间通信机制。 **2. 常用信号略述** 例如SIGINT、SIGTERM等。 **3. signal(2)函数** 用于捕获信号并指定...
此外,还讨论了如何撤销进程、孤儿进程和僵尸进程的处理方法。 - **通过Shell命令创建进程**: - 使用`file`命令查看文件类型。 - 使用`ps`命令查看当前系统中的进程信息,了解进程的状态和所运行的终端。 - 使用...
- **孤儿进程组**:当进程组的领导者退出时,该组变为孤儿。 #### 第十章 信号 - **基本概念**:信号是一种异步通知机制。 - **常用信号**:如SIGINT、SIGKILL、SIGTERM等。 - **signal(2)函数**:用于设置信号...
孤儿进程组(orphanedprocessgroups)** - 当会话领导者退出时,其进程组成为孤儿。 #### 第十章 信号 **1. 基本概念** - 信号是操作系统发送给进程的通知。 **2. 常用信号略述** - 包括`SIGINT`、`SIGTERM`、`...
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 ...
9.10 孤儿进程组 9.11 FreeBSD实现 9.12 小结 习题 第10章 信号 10.1 引言 10.2 信号概念 10.3 signal函数 10.4 不可靠的信号 10.5 中断的系统调用 10.6 可重入函数 10.7 SIGCLD...
- **孤儿进程组**:没有父进程的进程组。 - **4.3+BSD实现**:特定实现的细节。 #### 第10章:信号 - **信号的概念**:操作系统向进程发送的通知。 - **signal函数**:设置信号处理器。 - **不可靠信号**:可能...
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...
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...
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 不可靠的...
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...
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 ...
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...
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...