✧(≖ ◡ ≖✿
目录
进程组
进程组的分类
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 .gitignorebg 的使用
# 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。
设置SIGCHLD为SIG_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
文件黑洞,文件垃圾箱。写入的内容全部丢弃,读取的内容全部为空。
感谢支持,长期连载
欢迎关注