一、背景
一般情况下,启动终端(shell),系统会创建一个会话(shell 进程是会长),经过后续各种操作,该会话中会存在多个进程组,每个进程组中也会有多个进程(父进程是组长),若此时关闭 shell,内核会给 shell 发送 SIGHUP(1 号)信号,shell 又会给所有进程发送 SIGHUP,这样会使该会话中所有进程结束,但有时候有需求:关闭终端,也不结束某个进程,因此出现守护进程的概念。
二、铺垫知识
(1)进程组:多个进程组成一个进程组
(2)谁是进程组的组长?
组里的第一个进程(如果该进程组是有父进程 fork 而来,父进程默认是组长进程)
进程组 ID == 进程组长的 ID
三、会话
会话:多个进程组组成一个会话(shell 默认是会长进程),此处要会创建会话成为会长。
1、创建一个会话的注意事项
(1)组长不能是会长 ,若组长创建会话,会报错。fork 后,父进程是组长,意味着父进程不能是会长,会长只能由子进程担当。
(2)成为新会话的会长会丢弃原有的控制终端,(此时子进程自成一体,不属于 shell 会话中)。
2、 一般步骤
一般步骤:先 fork,父亲死,儿子创建会话(setsid)。
创建一个会话 :
pid_t setsid(void) // 成功:返回新会长 ID ;失败:返回 -1
pid_t pid = fork(); // 1.先创建子进程
if(pid > 0) {
exit(1); // 或者 kill(getpid(), SIGKILL) // 2.父亲死
}
else if(pid == 0) {
setsid(); // 3.子进程变成会长
while(1); // 让守护进程一直存在,有时候在此执行核心操作
}
四、 守护进程的特点
(1) 后台服务进程
(2) 不受终端控制
(3) 周期性的执行某项任务
(4) 不受用户登录注销影响
(5) 一般采用以 d 结尾的名字(也称为服务)
五、创建守护进程模型
创建守护进程模型:前 2 步是必须的,后 4 步非必须。
(1)创建子进程,父进程退出。
(2)子进程创建新会话(该子进程既是会长也是组长:一个进程构成一个进程组,构成一个会话)。
(3)改变当前工作目录 int chdir(const char *path) ,避免因为原工作目录由于某种原因失效,影响进程。
(4)重设文件掩码 umask。
(5)关闭文件描述符:因为已经脱离终端,所以标准输入、输出、错误没有用了。
(6)执行核心工作。
六、创建守护进程的命令(野套路)
nohup 命令:可以让他之后的进程不受到 SIGHUP 信号(需要和&配合使用)
运行时候: nohup ./a.out &
七、创建新会话的原因
(1)守护进程的意义是关闭终端,守护进程仍然执行
(2)对于普通进程,关闭终端,终端 shell(会长)会给所有成员发一个 SIGHUP 信号,终止会话中的所有进程。
(3) setsid,使原会话中的一个进程变为其他会话的会长,即脱离终端会话,这样终端关闭后,守护进程就不会因为 SIGHUP 而被终止。