进程组、会话、守护进程
2026/9/17 2:46:13 网站建设 项目流程
@bit::Shadow
✧(≖ ◡ ≖✿

目录

进程组

进程组的分类

sleep的后台运行举例

进程组VS任务

组长进程

会话

会话、任务、进程组辨识

常用指令

jobs 的使用

bg 的使用

守护进程(精灵进程)

守护进程VS后台进程VS前台进程

代码创建

信号忽略

工作路径、日志系统

模拟实现守护进程

设备文件:/dev/null


进程组

进程组是以一个/多个进程为单位的多/单进程载体。

进程组ID(PGID)若进程组ID相同代表它们是属于一个进程组。

像自定义协议服务中一个服务端,多个客户端就是一个进程组。

进程组的分类

每个会话内只有两种进程组类型:前台进程组、后台进程组

前台进程组:可以接受键盘输入的进程组(任务)。(像shell登录时创建的含-bash进程的进程组)

【shell启动进程组查询图】

后台进程组:即在后台运行的进程组(通过ps ajx查询),不接受键盘指令。(运行方式:./a.out &)在运行指令后加上”&“符号。

sleep的后台运行举例

运行如下指令指在后台进程组运行

sleep 1000 | sleep 2000 | sleep 3000 &

进程组VS任务

进程组是操作系统层面的概念而任务是用户层面的概念。

(本问题:具备混淆性,难以区分。)

组长进程

PGID与PID相同的进程为组长进程。(最多一个)

进程组的生命周期:子进程组创建开始到组内最后一个进程离开为止

组长进程的作用:代表整个进程组接受信号(接收到后分发给组员)。获取控制终端,设置前台进程组:
{
tcsetpgrp(fd, pgid); // 将进程组设为前台
}

会话

会话、任务、进程组辨识

一个会话内可以可以有多个进程组。

会话也有自己的ID叫做“SID”。

图示关系

一个会话中可以有多个进程组,但只有一个前台进程组,可以有多个后台进程组。

常用指令

jobs 显示后台任务列表。
fg [任务号] 将后台任务提到前台。
Ctrl + c 终止前台任务。(资源回收)
Ctrl + z暂停进程/进程组,自动切换到后台。
bg [任务号] 让任务运行在后台。

jobs 的使用

[ubuntu@localhost ~]$ jobs [1] Running sleep 100 & [2] Running tail -f server.log [3]+ Stopped vim CMakeLists.txt [4]- Running ./serverTCP 8080 [5] Done ls -l | grep txt [6] Stopped nano .gitignore

bg 的使用

# 1. 启动一个前台任务 $ sleep 300 # 2. 按 Ctrl+Z 暂停它 ^Z [1]+ Stopped sleep 300 # 3. 使用 bg 让它在后台继续运行 $ bg %1 [1]+ sleep 300 & # 4. 现在这个任务在后台运行,你可以继续输入其他命令 $

守护进程(精灵进程)

解决目标:目前我们创建的程序受用户登录/退出影响,像启动的计算服务一旦xshell关闭客户端使用必定失效。为解决用户状态导致的服务端终止因此产生了精灵进程

定义:将需要不断运行的(不受用户登录、退出的影响的)进程(组)开辟至一新会话中,此会话就不会受用户的影响了。(在后台运行)

守护进程具有:“独立性” “孤儿性” 。

守护进程VS后台进程VS前台进程

后台、前台进程是隶属于一个会话的相对概念(的进程组)。

代码创建

为当前进程创建一个新的会话,并将当前进程PID设置为新会话ID(SID)。

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

注意:
1.
调用setsid()的进程不能为组长进程(否则报错)。(因此我们常采用夫父进程直接退出,子进程创建新会话的操作)
2.守护进程无控制终端。如果存在在调用后会切断。

信号忽略

常设置信号忽略SIGCHLD与SIDPIPE。

设置SIGCHLDSIG_IGN,子进程会自动被回收,不会产生僵尸进程。

当进程向一个已关闭socket写入数据时,内核会发送SIGPIPE信号到写入进程导致服务端崩溃!!

工作路径、日志系统

工作路径:指进程组运行的根路径。(常切换(chdir)到根目录“/”)

1.所有路径索引均基于工作路径!!!
2.此外还会影响编译时文件的链接。

日志系统基于工作路径的索引文件,将日志写入。(opendup2()close)

模拟实现守护进程

/*守护进程译作:daemon (dei men)*/ const char *workpath = "/"; void Daemon (int cdir, int noclose) { //解决IO问题 signal (SIGCHLD, SIG_IGN); signal (SIGPIPE, SIG_IGN); if (fork () > 0) exit (0); //子进程创建作为新会话组组长 pid_t ret = setsid (); if(ret < 0) RETFATAL ("setsid"); //工作路径是否切换?(即守护进程运行的路径) if(cdir == 0) chdir (workpath); // 重定向标准输入、标准输出、标准错误到my.log文件(日志文件) // 这样做的要求是守护进程特征(默认关闭终端) if(noclose == 0) { int fd = open ("/my.log", O_RDWR);//读写 if(fd < 0) RETFATAL ("open"); int ret1 = dup2 (fd, 0); int ret2 = dup2 (fd, 1); int ret3 = dup2 (fd, 2); if(ret1 < 0 || ret2 < 0 || ret3 < 0) RETFATAL ("dup2"); //打开的fd需要关闭吗?--->关闭后会影响写入吗?不会!!! // int ret4 = close (fd); // if(ret4 < 0) // RETFATAL ("close"); } }

极其容易混淆的点:int dup2(int oldfd, int newfd); 此处是将newfd的指向重定向到oldfd。(而非旧到新)

设备文件:/dev/null

文件黑洞,文件垃圾箱。写入的内容全部丢弃,读取的内容全部为空。

感谢支持,长期连载
欢迎关注

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询