1. 守护进程是什么?
守护进程也称精灵进程(Daemon),是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事情。守护进程是一个很有用的进程,Linux的大多数服务器就是用守护进程实现的。同时,守护进程也可完成许多系统任务。
Linux系统启动时会启动很多系统服务进程,这些系统服务进程没有控制终端,不能直接和用户交互,其他进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但系统服务进程不受用户登录注销的影响,仍然在运行着,称这种进程为守护进程。
2. 创建守护进程
步骤如下:
(1)首先,调用umask将文件模式创建屏蔽字设置为0。
(2)然后,调用fork,父进程退出(exit)。
原因如下:
①如果该守护进程是作为一条简单的shell命令启动的,那么父进程终止使得shell认为该命令已经执行完毕。
②保证子进程不是一个进程组的组长进程。
(3)调用setsid创建一个新会话。
setsid会导致:
①调用进程称为新会话的首进程;
②调用进程成为一个进程组的组长进程;
③调用进程没有控制终端。
(4)将当前工作目录更改为根目录。
(5)关闭不再需要的文件描述符。
(6)忽略SIGCHID信号。
3. 编写代码
创建守护进程最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader。
#include <unistd.h>
pid_t setsid(void);
该函数调用成功时返回新创建的Session的id(其实也就是当前进程的id),出错返回-1。注意,调用这个函数之前,当前进程不允许是进程组的Leader,否则该函数返回-1。要保证当前进程不是进程组的Leader也很容易,只要先fork再调用setsid就行了。fork创建的子进程和父进程在同一个进程组中,进程组的Leader必然是该组的第一个进程,所以子进程不可能是该组的第一个进程,在子进程中调用setsid就不会有问题了。
下面自己创建一个守护进程,代码如下:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
void mydaemon()
{
umask(0); //设置文件掩码为0
if(fork()>0)
{
exit(0); //终止父进程
}
setsid(); //设置新会话
chdir("/");
close(0);
close(1);
close(2);
signal(SIGCHLD,SIG_IGN);
}
int main()
{
mydaemon();
return 0;
}
4. 为什么创建守护进程时有人fork两次?
fork两次的主要目的是:
防止进程再次打开一个控制终端。因为打开一个控制终端的前台条件是该进程必须是会话组长,而再fork一次,子进程id不再是sid(sid是父进程的sid),因此也无法打开新的控制终端。