4 进程关系(第9章)
4.1 进程组
每个进程除了有个唯一的进程ID之外,还属于一个进程组,进程组是一个或多个进程的集合,同一进程组中的各进程接收来自同一终端的各种信号;
每个进程组有一个唯一的进程组ID,每个进程组有一个组长进程,组长进程的进程组ID等于其进程ID,即每个进程组的进程组ID就是其组中组长进程的进程ID;进程组ID是一个正整数,存放在pid_t数据结构中;
只要某个进程组中有一个进程存在,无论组长进程是否终止,该进程组依旧存在,并且某个进程组的最后一个进程可以终止,也可以转移到另一个进程组中;
从进程组创建开始到其中最后一个进程离开为止的时间区间称为进程组的生命期;
一个进程只能为它自己或它的子进程设置进程组ID。
4.2 会话
会话是一个或多个进程组的集合;
通常通过shell的管道将几个进程编成一组;
使用setsid创建新会话:
#include <unistd.h>
pid_t setsid(void); 成功则返回进程组ID,失败则返回-1;
调用此函数的进程不能是一个进程组的组长,否则返回出错,并且该进程变成新会话的会话首进程;保证创建会话的进程不为组长进程的方法是:
先调用fork,然后使其父进程终止,则子进程继续,这时子进程继承了父进程的进程组ID,而进程ID则是新分配的,这样保证了子进程不是进程组的组长。
getsid函数返回会话首进程的进程组ID,如下:
#include <unistd.h>
pid_t getsid(pid_t pid); 成功则返回会话首进程的进程组ID,出错则返回-1;
当pid=0时,getsid函数返回调用进程的会话首进程的进程组ID。
4.3 控制终端
一个会话可以有一个控制终端,通常是终端设备或伪终端设备;
建立与控制终端连接的会话首进程被称为控制进程;
一个会话中的几个进程组可被分成一个前台进程组以及一个或多个后台进程组;如果一个会话有一个控制终端,则它有一个前台进程组,其他进程组均为后台进程组;
无论何时键入终端的中断键(常为Delete或Ctrl+C),都会将中断信号发送给前台进程组的所有进程;键入退出键(常是Ctrl+\),都会将退出信号发送给前台进程组的所有进程;
如果终端接口检测到调制解调器(或网络)已经断开连接,则将挂断信号发送至控制进程,即会话首进程;
登录时,会自动建立控制终端;
保证程序能与控制终端对话的是open文件/dev/tty,在内核中,此特殊文件是控制终端是同义语;
4.4 作业控制
一个作业只是几个进程的集合,通常是一个进程管道;用户可以在前台或后台启动一个作业;
有3个特殊字符可使终端驱动程序产生信号,并将它们发送至前台进程组,分别是中断字符产生SIGINT,退出字符产生SIGQUIT,挂起字符(一般为Ctrl+Z)产生SIGTSTP;只有前台作业接收终端输入,如果后台试图读终端,需要停止后台作业并用shell命令将其转为前台作业运行后可读终端信息。
当启动一个后台作业时,shell赋予它一个作业标识符,并打印一个或多个进程ID。
4.5 孤儿进程组
一个其父进程已终止的进程称为孤儿进程,这种进程由init进程收养,若整个进程组也成为了孤儿,则为孤儿进程组。
关于孤儿进程组的说法:1,该组中每个成员的父进程要么是该组的成员,要么不是该组所属会话的成员;2,该组中有一个进程,其父进程在属于同一会话的另一个组里,进程组中每一个进程的父进程都属于另一个会话;