本文共 2405 字,大约阅读时间需要 8 分钟。
守护进程也叫精灵进程(Daemon),它在后台运行,独立于控制终端并周期性地执行某种任务或等待某些事情发生,一般用于C/S 服务中,在服务端监听端口,等待客户端的连接,如果有客户端发出连接,则fork出一个子进程去响应,而守护进程继续监听。
创建一个守护进程一般有如下几步:
umask(0)
,将文件模式创建屏蔽字改为0fork()
,父进程退出,目的是: ssetsid()
,会导致如下结果: chdir("/")
将当前工作目录更改为根目录,目的是防止用户改动目录,从而影响进程的运行关于忽略SIGCHLD信号:父进程fork出一个进程,然后父进程立马退出, 在子进程的执行流中, 设置 signal(SIGCHLD, SIG_IGN)。 此时原来的父进程已经不存在,而子进程脱离终端,自成会话, 相当于 子进程就变成了新会话的父进程, 又因为此时它是守护进程, 代码已经写好了, 而在它的执行流中很有可能会再次 fork 产生新的子进程, 此时如果它fork出的子进程退出的话,必须要回收资源,可以使用wait 或者 waitpid, 但是这样会导致守护进程阻塞, 所以 在守护进程中捕捉SICCHLD信号, 并忽略, 可以让守护进程不阻塞,并回收子进程资源。
#include#include #include #include #include #include #include void create_daemon(){ //1. 将文件模式创建屏蔽字设置为0 umask(0); //2. 调用fork 父进程退出 // 作用:确保子进程不是一个进程组的组长 pid_t id = 0; if ( (id = fork()) < 0) { perror("fork"); exit(2); } if(id > 0 ) { // father exit(3); } // 3.调用setsid 创建一个新的会话 setsid(); //4. 将当前工作目录改为更目录 chdir("/"); //5. 关闭不需要的文件描述符 close(0); close(1); close(2); //6. 忽略SIGCHLD 信号 signal(SIGCHLD, SIG_IGN); // 守护进程逻辑 while(1) { // Do something }}int main(){ create_deamon(); return 0;}
#includepid_t setsid(void);成功返回新创建的会话 ID ,出错返回-1
调用此函数之前,当前进程不允许是进程组的Leader,如果是则出错返回-1。
所以可以先fork创建子进程,然后在调用setsid,fork出的子进程和父进程在同一个进程组中,进程组的Leader必然是父进程,所以可以保证该函数会执行成功。该函数成功的表现:
- 创建一个新的会话,当前进程成为会话Leader,SID = PID - 创建一个新的进程组,当前进程称为进程组的Leader,GID = PID - 当前进程与控制终端脱离关系下面内容于修改:2017年6月19日19:00:10
有时候创建守护进程时会fork 两次,原因如下:
对于原因1的解释:
APUE 第13 章中关于守护进程的介绍,作者在小字部分提到,在System V 环境下,有人建议 fork 两次,为了保证守护进程不是会话首进程,(因为会话首进程可以获取到控制终端,而非会话收进程无法打开控制终端),避免它取得控制终端。所以说第二次fork 可以在你确保不获取控制终端的情况下略之。
对于原因2的解释:
这里有一个假定,父进程生成守护进程后,还有自己的事要做,它的人生意义并不只是为了生成守护进程。这样,如果父进程fork一次创建了一个守护进程,然后继续做其它事时阻塞了,这时守护进程一直在运行,父进程却没有正常退出。如果守护进程因为正常或非正常原因退出了,就会变成ZOMBIE进程。
如果fork两次呢?父进程先fork出一个儿子进程,儿子进程再fork出孙子进程做为守护进程,然后儿子进程立刻退出,守护进程被init进程接管,这样无论父进程做什么事,无论怎么被阻塞,都与守护进程无关了。所以,fork两次的守护进程很安全,避免了僵尸进程出现的可能性。
转载地址:http://xzexi.baihongyu.com/