【一】会话
'会话'可理解为一个区域,通常一个用户登录对应一个会话。每个'会话'相互独立,关闭一个不会影响其他会话。在一个会话内包含多个进程,例如解析 OS 命令的应用程序(如 bash),它处于 OS 与用户中间。
注意:bash 进程不是 OS 的子进程,而是用来解析用户命令给 OS 的应用程序子进程。

【二】前/后台进程
谁是前台或后台进程取决于谁拥有键盘。例如,Ctrl+C 信号只对当前的前台进程有效。每个会话区只允许存在一个前台进程,其余为后台进程。
前台切后台进程
在命令末尾加 &,进程会被丢到后台运行(例如:./Ceshi &)。此时它变为了后台进程,无法直接读取键盘输入。


查看后台进程
执行指令 jobs,即可查看后台进程,其中最前面的数字是作业编号(理解为后台进程编号)。

后台切前台进程
执行指令 fg %作业号 可以把后台作业拉到前台运行。

暂停与继续
这里我们采用 19 号信号来暂停后台进程(fg 作业号 是切换到前台,我习惯用 19 号信号暂停)。
继续运行后台进程:执行指令 bg 作业号 即可让后台暂停的进程继续运行。
【三】进程组与守护进程
(1)查看进程组
一个会话中存在多个进程组,即相关联的进程集合。

注意:前台进程组是唯一的,但前台进程组并非只有一个进程,比如:
ls -l | grep ".txt" | wc -l。
进程组的创建者(第一个进程)就是该进程组的组长(组长进程的 PID 就是组员的 PGID)。组内所有进程都继承组长的 PGID,直到组长退出或进程被迁移到其他组。
简单粗暴,可使用指令:ps -efj
-e: 显示系统内所有进程-f: 显示完整的信息(包括 UID, PID, PPID, C, STIME, TTY, TIME, CMD)-j: 显示与作业控制相关的信息(包括 PGID, SID)

参数含义:
| 字段 | 含义 | 与进程组的关系 |
|---|---|---|
| UID | 用户 ID | 进程的所有者 |
| PID | 进程 ID | 每个进程唯一的标识符 |
| PPID | 父进程 ID | 创建该进程的进程 ID |
| PGID | 进程组 ID | **核心字段!**同一个进程组内的所有进程拥有相同的 PGID。通常,进程组的组长进程的 PID 等于 PGID |
| SID | 会话 ID | 进程所属会话的 ID。一个会话可以包含多个进程组 |
| C | CPU 使用率 | 进程使用的 CPU 百分比 |
| STIME | 启动时间 | 进程开始运行的时间 |
(2)守护进程
守护进程是运行在后台的特殊进程,它独立于控制终端,不受用户登录或注销的影响。如何理解:我们的计算机桌面窗口可以理解为一个会话窗口,守护进程单独成一个会话窗口。

(3)守护进程原理
主要原理:原进程 A 由父进程管理,而父进程主动退出,子进程 A 就变为了孤儿进程,父进程变为该会话的第一个进程,比如 bash 进程,再通过系统调用让 A 进程脱离这个终端/会话(不接受终端退出时的信号操作),成为守护进程(kill -9 -19 这两个特殊的信号除外)。
(4)如何创建守护进程
创建子进程,父进程主动退出:
pid_t pid = fork();
if (pid > 0) exit(0);
else {
// 子进程继续执行
}
子进程单独成为会话:使子进程成为会话首进程,脱离原终端
setsid();
忽略一定的信号:
signal(SIGHUP, SIG_IGN);
signal(SIGSTOP, SIG_IGN);
// ...
切换工作目录:
chdir("/");
设置文件权限掩码为 0:确保创建的文件和目录拥有最大的权限
umask(0);
可以将守护进程的日志信息打印到
/dev/null(垃圾站):
const char* ptr = "/dev/null";
int fd = open(ptr, O_RDWR);
if (fd > 0) {
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
}


