守护进程
它是特殊的进程。它独立于控制终端并周期性的执行某种任务或等待处理某些中断事件。其他进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止(因为用户注销就代表着会话注销,则这个会话中所有的进程都会终止)。
在Linux系统启动时,会启动很多系统服务进程,这些进程就是守护进程。通常为用户提供服务。我们可以通过ps axj 指令查看到当TPGID(前台进程组id)为-1时都为没有终端的进程,也就是守护进程。
守护进程代码
创建守护进程的几个步骤
1. 调用umask(0),使得改进程创建文件不会被umask影响。(列如:ipc中 创建管道和消息队列)
2. 调用fork,使父进程退出(原因1 这里使父进程退出,使bash收到父进程发出的SIGCHID信号由后台切换至前台。原因2 要为后面调用setsid时,得保住调用进程不能为组长进程和会首进程。)
3. 调用setsid
4. 可以再次调用fork(这里有些写法又fork了次,第二次fork主要为了防止守护进程再次打开终端文件与之关联。因为只有会首进程可以打开终端文件与之关联,所以又fork了次,让会首进程退出,此时该守护进程无法申请与其他终端文件相关联。)
5. 屏蔽SIGCHID信号。
6. 调用chdir改变主工作目录为根目录
7. 关闭不在需要的文件描述符
tip:
pid_t setsid(void)
调用该函数要确保,调用进程不是组长进程,因为调用该函数后,会生成一个新会话和新进程组,并把该进程作为会话的首进程和组长进程。如果该进程是组长进程,那么该进程到底是新进程组的组长还是以前进程组的组长呢?所以为了防止这种二义性问题出现所以如果进程组长调用会失败。
- 生成一个新会话,调用进程成为这个会话的 Leader
- 生成一个新进程组,调用进程成为这个进程组的Leader
- 与之前的控制终端去关联。(这里并不是指关闭以前终端的相应的文件描述符,只是之前的终端文件此时是一个普通文件)
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void create_demon()
{
umask(0);
pid_t id;
if((id=fork())>0) exit(0);
else
{
struct sigaction act;
act.sa_flags=0;
sigemptyset(&act.sa_mask);
act.sa_handler=SIG_IGN;
sigaction(SIGCHLD,&act,NULL);
setsid();
if((id=fork())>0) exit(0);
else
{
close(0);
open("/dev/null",O_RDWR);
close(1);
dup(0);
close(2);
dup(0);
chdir("/");
}
}
}
int main()
{
create_demon();
while(1)
{
sleep(1);
}
return 0;
}