|
|
author:anuode
先介绍一个系统调用函数:setsid()
setsid()创建一个会话并设置GID。加入调用进程不是一个进程组的领导者进程的话,该进程将创建一个新的会话,在这种情况下,将使调用进程成为该 新会话的领导者进程,也成为该新进程组的领导者进程,并且没有控制终端,最后,在这个会话中将只有这一个进程组,这个进程组中也只有这一个进程。
编写daemon程序的规则:
1.在后台运行 为避免挂起控制终端,要将daemon仿如后台执行。其方法是,在进程中调用fork使父进程终止,让daemon子进程继续在后台运行。 if(pid=fork()) exit(0);
2.脱离控制终端,登陆会话和进程组 登陆控制终端包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登陆终端。控制终端,登陆会话和进程组通常是从父进程继承下来 的。我们的目的就是摆脱它们,使之不受它们的影响。 方法就是调用setsid();
3.禁止进程重新打开控制终端 现在通过2的setsid调用,进程已经是会话的组长,它可以重新申请打开一个控制终端。可以再次调用fork()使进程不再是会话的组长就可以避免进程 重新打开控制终端: if(pid=fork()) exit(0);
4.关闭打开的文件描述符 for(i=0;i<OPEN_MAX;i++) close(i);
5.改变当前工作目录 进程活动时,其工作目录所在的文件系统不能卸载。一般使用chdir(“/”)将工作目录改变到根目录。
6.重设文件创建掩码 进程从创建它的父进程那里继承了文件创建的掩码,它可以修改daemon进程创建的文件的权限。为防止这一点可以将文件创建掩码清除掉: umask(0);
7.处理SIGCHLD信号 处理此信号并不是必需的。可以简单的将其忽略掉: signal(SIGCHLD,SIG_IGN);
例:
extern int daemon_proc;
int daemon_init(const char *pname,int facility)
{
int i;
pid_t pid;
/*-------创建一个子进程,并结束父进程------*/
if((pid = fork())<0)
return(-1);
else
{
if(pid)
_exit(0);
}
/*-------儿子进程继续执行-------*/
if(setsid()<0)
return(-1); /*使儿子进程1变为新会话的领导者进程*/
/*------忽略掉SIGHUP信号-----*/
signal(SIGHUP,SIG_IGN);
/*------创建孙子进程,并结束儿子进程-------*/
if((pid=fork())<0)
return(-1);
else
{
if (pid)
_exit(0);
}
/*-------孙子进程继续执行--------*/
daemon_proc =1 ; /*告诉出错处理函数,当出现错误是,是去写日志而不是将错误输出到stderr*/
chdir("/");
umask(0);
/*-------关闭前64个描述符------*/
for(i=0;i<64;i++)
close(i);
/*--------重定向标准输入、标准输出和标准出错到/dev/dull--------*/
open("/dev/null",O_RDONLY);
open("/dev/null",O_RDWR);
open("/dev/null".O_RDWR);
openlog(pname,LOG_PID,facility);
return(0);
}
说明:第二次fork()的原因是要确保daemon在将来不会自动请求控制终端当打开一个终端设备时。由于经过第一次fork()后,儿子进程就变成了 新会话的领导者进程且没有控制终端,当它打开一个终端设备的时候,这个终端就变成了会话领导者进程的控制终端。通过第二次fork(),可以保证孙子进程 不会是一个会话的领导者进程,这样它就不能够请求一个控制终端。 |
|