守护进程(daemon进程)是后台守护进程,有时候也叫精灵进程(agent).
linux 下server都是daemon进程。
其 特点 是:
1)其父进程是一号进程,通常以d结尾
2)在后台运行,独立于终端,周期性的以某种任务或等待处理某些发生的事
3)自成进程组,自成会话,不受登陆注销等影响
4)一般是孤儿进程
daemon函数存在的原因是因为控制终端由于某些原因(如断开终端链接)会发送一些信号的原因。而接收进程处理这些信号缺省动作会让进程退出。这些信号会由于终端上敲一些特殊按键而产生。
创建守护进程最关键 的⼀一步是 调⽤用setsid函数 创建⼀一个新的Session,并成为Session Leader。
该函数调⽤用成功时返回新创建的Session的id(其实也就是当前进程的id),出错返回-1。注意,调用这个函数之前,当前进程不允许是进程组的Leader,否则该函数返回-1。要保证当前进程不 是进程组的Leader也很容易,只要先fork再调用setsid就行了。fork创建的子进程和父进程在同 ⼀一个进程组中,进程组的Leader必然是该组的第一个进程,所以子进程不可能是该组的第一个进程,在子进程中调⽤用setsid就不会有问题了。
成功调用该函数的结果是:
1. 创建⼀一个新的Session,当前进程成为Session Leader,当前进程的id就是Session的id。
2. 创建⼀一个新的进程组,当前进程成为进程组的Leader,当前进程的id就是进程组的id。
3. 如果当前进程原本有⼀一个控制终端,则它失去这个控制终端,成为一个没有控制终端的进 程。所谓失去控制终端是指,原来的控制终端仍然是打开的,仍然可以读写,但只是一个普通的打开文件而不是控制终端了。
创建守护进程
1. 调用umask将文件模式创建屏蔽字设置为0.
2. 调用fork,父进程退出(exit)。原因:1)如果该守护进程是作为一条简单的shell命令启动的,那么父进程终止使得shell认为该命令已经执行完毕。2)保证子进程不是一个进程组的组长进程。
3. 调用setsid创建⼀一个新会话。setsid会导致:1)调用进程成为新会话的⾸首进程。 2)调用 进程成为一个进程组的组长进程 。3)调用进程没有控制终端。(再次fork⼀一次,保证 daemon进程,之后不会打开tty设备)
4. 将当前工作目录更改为根目录。
5. 关闭不在需要的文件描述符。
6. 其他:忽略SIGCHLD信号
了解详细守护进程创建内容:点击打开链接
创建简易的守护进程(两次fork())
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<signal.h>
void mydaemon()
{
umask(0);//1
if(fork()>0)//2
exit(1);
setsid();//3
if(fork()>0)
exit(1);
chdir("/");//4
close(0);//5
close(1);
close(2);
signal(SIGCHLD,SIG_IGN);//6
}
int main()
{
mydaemon();
while(1)
{
sleep(1);
}
return 0;
}
使用ps axj查看系统中的所有进程,通过管道过滤出自己创建的守护进程,可以发现进程组id和会话id都与pid不同。
1 、第一次fork的作用是让shell 认为本条命令 已经终止,不用挂在终端输入上。还有一个作用是为后面setsid服务。setsid的调用者不能是进程组组长(group leader). 此时父进程是进程组组长。
2 、setsid() 是本函数最重要的一个调用。它完成了daemon函数想要做的大部分事情。调用完整个函数。子进程是会话组长(sid==pid),也是进程组组长(pgid == pid),并且脱离了原来控制终端。到了这一步,基本上不管控制终端如何怎么样。新的进程都不会收到那些信号。
3 、经过前面2个步骤,基本想要做的都做了。第2次fork不是必须的。也看到很多开源服务没有fork第二次。fork第二次主要目的是。防止进程再次打开一个控制终端。因为打开一个控制终端的前台条件是该进程必须是会话组长。再fork一次,子进程ID != sid(sid是进程父进程的sid)。所以也无法打开新的控制终端。