一、引言
进程组、会话 和 控制终端 是 Linux 系统进程管理和作业控制的核心概念。
会话与进程组之间的关系就像是公司与部门之间的关系。

二、进程组
概念
进程组是由一个或多个相关联的进程组成的集合,主要用于统一管理和控制这些进程。每个进程都有一个进程组 ID(PGID),用于标识其所属的进程组。进程组一般由组长进程创建,进程组 ID 就是组长进程 ID。
进程组有前台进程组和后台进程组。
前台进程组会阻塞控制终端,能够向终端设备进行读、写操作。
后台进程组不能不能向终端进行读操作,只能向终端进行写操作。
销毁
如果进程组组长进程终止,进程组仍然存在直到组内所有进程都结束,并且组长进程终止不会改变进程组的 PGID。
功能
其他进程可以发送信号给整个进程组,从而影响组内的所有进程。
加入
加入进程组有两种方式:
默认加入:当一个进程通过 fork() 创建子进程时,子进程默认继承父进程的进程组 ID(PGID),因此父子进程位于同一个进程组。
主动加入:进程可以通过调用 setpgid() 或 setsid() 系统调用来改变其所属的进程组。setpgid() 可以将进程加入指定的进程组,而 setsid() 则会创建一个新的会话,并将调用进程设置为该会话中新进程组的组长。
常用函数
1
2
3
4
5
// 获取进程组 ID
pid_t getpgrp(void);
// 修改进程组 ID 为 pid,如果 pid == 0,则修改进程组 ID 为调用进程的 ID
int setpgid(pid_t pid, pid_t pgid);
三、会话
概念
会话是一个或多个进程组的集合,通常由用户通过登录终端启动。创建会话的进程称为会话首领(Session Leader),其进程 ID(PID)即为会话 ID(SID)。会话可以与一个控制终端相关联,负责处理用户的输入输出。
销毁
当用户关闭控制终端时,系统会先该终端关联的会话中的所有前台和后台进程发送 SIGHUP(挂起)信号。默认情况下,这些进程会因接收到 SIGHUP 信号而终止。但如果有进程在接收到 SIGHUP 信号后继续运行(例如,被配置为忽略该信号),那么会话将继续存在,直到最后一个进程结束。
功能
会话的引入主要用来管理与用户交互的环境。
加入
加入会话也有两种方式:
默认加入:当用户登录系统并启动一个 Shell 时,系统会为该登录创建一个新的会话。此后,所有从该 Shell 派生的进程(通过 fork() 创建)默认属于同一会话,继承相同的会话 ID(SID)。
主动加入:进程可以通过调用 setsid() 系统调用创建一个新的会话。调用该函数的进程不能是当前进程组的组长。成功调用后,进程将:
- 成为新会话的会话首领,SID 等于其 PID。
- 成为新进程组的组长,PGID 等于其 PID。
- 脱离原有的控制终端(如果有的话),新会话没有控制终端。
常用函数
1
2
3
4
5
// 获取会话 ID
pid_t getsid(pid_t pid);
// 创建新会话,注意:调用该函数的进程不能是当前进程组的组长。
pid_t setsid(void);
四、控制终端
控制终端是与会话关联的终端设备,用于处理用户的输入和输出。当用户通过终端登录系统时,系统会为该会话分配一个控制终端。
一个会话只能有一个控制终端。当用户在终端中运行命令时,这些命令所属的进程通常是前台进程组的一部分,可以直接接受用户输入。而使用 & 符号将命令放入后台运行时,相关进程则属于后台进程组。