Linux守护进程的创建
Linux守护进程又称为Daemon进程,为Linux的后台服务进程(独立于控制终端)。该进程通常周期性地执行某种任务或等待处理某些发生的事件。其生命周期较长,通常在系统启动时开始执行,在系统关闭时终止。Linux中很多系统服务都是通过守护进程实现的。
在Linux中,每一个从终端开始运行的进程都会依附于一个终端(系统与用户进行交互的界面),这个终端为进程的控制终端。当控制终端关闭时,这些进程就会自动结束,但守护进程不受终端关闭的影响。
如何将一个进程变成一个守护进程,只需要遵循一些特定的流程。
1.创建子进程(子进程不退出,父进程退出)
很明显,由于父进程先于子进程退出,造成子进程成为孤儿进程。此时子进程的父进程变成init进程。
2.在子进程中创建新会话
使用的函数是 setsid() 。该函数将会创建一个新会话,并使进程担任该会话组的组长。同时,在会话组中创建新的进程组,该进程依然也是进程组的组长。该进程成为新会话组和进程组中唯一的进程。最后使该进程脱离终端的控制,运行在后台。
之所以需要这样处理,是因为子进程在被创建时,复制了父进程的会话、进程组和终端控制等。虽然父进程退出,但原先的会话、进程组和控制终端等并没有改变。因此,子进程并没有实现真正意义上的独立。
3.改变当前的工作目录
使用fork() 函数创建的子进程继承了父进程的当前工作目录。系统通常的做法是让根目录成为守护进程的当前工作目录。改变工作目录的函数是chdir()。
#include<unistd.h>
int chdir(const char *path);
4.重设文件权限掩码
文件权限掩码的作用是屏蔽文件权限中的对应位。文件被创建后,其用户操作权限mode,将会被执行mode&~umask(umask为文件权限掩码,通常用八进制数表示)。由于创建的子进程继承了父进程的文件权限掩码,这给子进程操作文件带来一定的影响。因此,通常把文件权限掩码设置为0,这样可以增强守护进程的灵活性。此时,文件权限掩码取反全为1,与任何文件权限相与,都可保持文件最原始的状态值。
使用函数umask(),改变文件权限掩码,参数即为要修改的掩码值。
#include<sys/types.h>
#include<sys/stat.h>
mode_t umask(mode_t mask);
5.关闭文件描述符
新创建的子进程会从父进程继承一些已经打开的文件描述符。这些描述符可能永远都不会被守护进程访问,但他们却占有一定的资源。特别注意的是,守护进程脱离了终端的控制,所以与终端相关的标准输入、输出、错误输出的文件描述符0、1、2,已经没有了任何价值,应当关闭。
for(i=0;i<getdtablesize();i++)
{
close(i);
}
其中,getdtablesize() 函数的功能为获取文件描述符表的大小,也可以理解为获取进程打开的文件描述符的最大数量。
其流程图为:
创建守护进程代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/stat.h>
int main(int argc,const char *argv[])
{
pid_t pid;
int i,fd;
char *buf = "This is a Daemon\n";
pid = fork(); //第一步
if(pid < 0)
{
perror("fork error");
return -1;
}
else if(pid > 0)
{
exit(0); //父进程瑞出
}
else
{
setsid(); //第二步
chdir("/tmp"); //第三步
umask(0); //第四步
for(i = 0;i < getdtablesize();i++) //第五步
{
close(i);
}
/* 守护进程创建完成,以下代码为进入守护进程的工作 */
if((fd = open("daemon.log",O_CREAT|O_WRONLY|O_TRUNC,0600)) < 0)
{
perror("open error");
return -1;
}
while(1)
{
write(fd,buf,strlen(buf));
sleep(3);
}
close(fd);
}
return 0;
}
终端输入ps axj ,可以查看守护进程信息,可以看到进程的ID与组ID、会话组ID保持一致,说明当前守护进程为组长。其父进程为init进程(ID为1),TTY选项为 "?" ,表示其为后台进程