APUE - 进程关系

进程组

每个进程除了有一个进程 ID 之外,还属于一个进程组。

进程组是一个或多个进程的集合。通常,它们与同一作业相关联,可以接收来自同一终端的各种信号。每个进程组有一个唯一的进程组 ID。

#include <unistd.h>
pid_t getpgrp(void);
pid_t getpgid(pid_t pid);

每个进程组都可以有一个组长进程。组长进程的标识是,其进程组 ID 等于其进程 ID。

只要在某个进程组中有一个进程存在,则该进程组就存在,这与其组长进程是否存在无关。

进程可以通过调用 setpgid 来加入一个现有的组或者创建一个新进程组。

#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);

一个进程只能为它自己或它的子进程设置进程组 ID。在它的子进程调用了 exec 函数之一后,它就不再能改变该子进程的进程组 ID。

会话

会话是一个或多个进程组的集合。

进程调用 setsid 函数建立一个新会话。

#include <unistd.h>
pid_t setsid(void);

如果调用此函数的进程不是一个进程组的组长,则此函数就会创建一个新会话,结果将发生下面 3 件事:

  1. 该进程变成新会话首进程(会话首进程是创建该会话的进程)。此时,该进程是新会话中唯一的进程。
  2. 该进程成为一个新进程组的组长进程。新进程组 ID 是该调用进程的进程 ID。
  3. 该进程没有控制终端。如果在调用 setsid 之前该进程有一个控制终端,那么这种联系也会被中断。

如果该调用进程已经是一个进程组的组长,则此函数返回出错。

getsid 函数返回会话首进程的进程组 ID。

#include <unistd.h>
pid_t getsid(pid_t pid);

控制终端

会话和进程组有一些其他特性:

  • 一个会话可以有一个控制进程。这通常是登录到其上的终端设备(在终端登录情况下)或伪终端设备(在网络登录情况下)。
  • 建立与控制终端连接的会话首进程被称为控制进程。
  • 一个会话中的几个进程组可以被分成一个前台进程组以及一个或几个后台进程组。
  • 如果一个会话有一个控制终端,则它有一个前台进程组,会话中的其他进程组则为后台进程组。
  • 无论何时键入终端的中断键,就会将中断信号发送给前台进程组的所有进程。
  • 无论何时键入终端的退出键,就会将退出信号发送给前台进程组的所有进程。
  • 如果终端接口检测到调制解调器(或网络)已经断开连接,则将挂断信号发送给控制进程(会话首进程)。

tcgetpgrp、tcsetpgrp 和 tcgetsid 函数

#include <unistd.h>
pid_t tcgetpgrp(int filedes);
int tcsetpgrp(int filedes, pid_t pgrpid);

#include <termios.h>
pid_t tcgetsid(int filedes);

作业控制

作业控制允许在一个终端上启动多个作业(进程组),它控制哪一个作业可以访问该终端,以及哪些作业在后台运行。

作业控制要求下面三种形式的支持:

  1. 支持作业控制的 shell。
  2. 内核中的终端驱动程序必须支持作业控制。
  3. 内核必须提供对某些作业控制信号的支持。