作者:gnuhpc
出处:http://www.cnblogs.com/gnuhpc/
1.简介:守护服务进程指的是在后台运行,起到提供服务的进程。
2.步骤:
1)将进程放入后台:
这里利用了fork为当前进程创建一份拷贝(即子进程),然后令父进程退出后子进程被init进程(系统初始化进程,是所有进程的父进程)接管时会将进程放入后台这个特点。
i=fork();if (i<0) exit(1); /* fork error */if (i>0) exit(0); /* parent exits *//* child (daemon) continues */
2)进程独立化
每一个进程都从它连接的终端获取信号,并且它也继承了其父进程的控制终端,一个守护进程不应该从开启它的主进程中获取信号,因此要设法从与其连接的控制终端中脱离出来。在unix中,会话(session)由一个或多个进程组的组成,进程组由一个或多个进程组成,会话与控制终端是一一对应的关系。
方法是:使用setsid将进程放入新的一个会话中,这个进程是这个新会话的leader,也是这个新进程组的leader,并且没有控制终端。这就达到了分离进程与控制终端的作用。
setsid();//use setpgrp() can also work
3)重定向标准IO描述符
在创建子进程时,打开的描述符会被子进程继承,这会造成资源的不必要浪费,所以应该在fork之前或者在子进程刚启动时把不需要的描述符关闭,但是要将三个标准IO描述符:标准输入(0)、标准输出(1)、标准错误(2),连接到一个没有任何不良影响的IO设备上,我们的做法是关闭所有的文件描述符,open打开/dev/null 这个设备,使其具有文件描述符0,也就是标准输入,然后使用dup将标准输出和标准错误都重定向到这个设备上去:
for (i=getdtablesize();i>=0;--i) close(i); /* close all descriptors */i=open("/dev/null",O_RDWR); /* open stdin */dup(i); /* stdout */
dup(i); /* stderr */
4)设置umask
安全起见,我们常常通过设置umask对创建文件的读写进行控制,根据Daemon在不同场景的需要我们可以设置不同的umask,满足权限控制(创建的新文件的权限是umask的补码)的要求,在某些文献上写umask设置为0,注意这是开放所有权限,请确定这样做不会带来风险:
umask(027);
5)设置运行目录
为了保证运行时的环境,我们通常要设定运行时相对的根目录,有些文档上统一设置为系统根目录,请确定这样做是否符合你的应用场景:
chdir("/servers/");
6)设置单例运行
大多数Daemon service程序都希望在同一时刻在内存中只有一个实例,那么常使用文件锁来满足这个需求,在这个文件锁中,我们写入这个进程的进程号pid:
lfp=open("exampled.lock",O_RDWR|O_CREAT,0640);
if (lfp<0) exit(1); /* can not open */if (lockf(lfp,F_TLOCK,0)<0) exit(0); /* can not lock *//* only first instance continues */
sprintf(str,"%d/n",getpid());
write(lfp,str,strlen(str)); /* record pid to lockfile */
7)处理信号
一个进程可以从用户或者一个进程接收信号并对其进行相应的处理,使用signal(提倡使用sigaction)绑定处理函数。
8)写日志
每个Daemon service都需要有日志记录功能以便用户查阅,有下列几种基本方法:
a)重定向所有输出到标准IO:在程序启动的时候使用shell的重定向功能。
b)log文件:打开一个文件并写入。
c)rsyslogd守护进程:使用这个系统守护进程,在/etc/rsyslog.conf进行配置完成。具体方法请参见:http://www.rsyslog.com/
3.实例:
/*
* =====================================================================================** Filename: daemon.c** Description:To test daemon: ps -ef|grep daemonexampled (or ps -aux on BSD systems)To test log: tail -f /tmp/daemonexampled.logTo test signal: kill -HUP `cat /tmp/daemonexampled.lock`To terminate: kill `cat /tmp/daemonexampled.lock`** Version: 1.0* Created: 10/25/2010 03:42:00 PM* Revision: none* Compiler: gcc** Author: gnuhpc , warmbupt@gmail.com* Company: IBM CDL** =====================================================================================*/#include #include#include <string.h>
#include #include #include#define RUNNING_DIR "/tmp"
#define LOCK_FILE "daemonexampled.lock"
#define LOG_FILE "daemonexampled.log"
void log_message(char *filename,char *message){FILE *logfile;logfile=fopen(filename,"a");
if(!logfile)
return;
fprintf(logfile,"%s/n",message);
fclose(logfile);}void signal_handler(int sig){switch(sig) {
case SIGHUP:
log_message(LOG_FILE,"hangup signal catched");
break;
case SIGTERM:
log_message(LOG_FILE,"terminate signal catched");
exit(0);break;
}}int main(void){int i,lfp;
char str[10];
if(getppid()==1) //Test if the process is running backgroundreturn 1; /* already a daemon */i=fork();if (i<0)
return 1; /* fork error */if (i>0)
return 0; /* parent exits *//* child (daemon) continues */
int sid = setsid(); /* obtain a new process group */if( sid<0 )
{exit 1;}for (i=getdtablesize();i>=0;--i)
close(i); /* close all descriptors */
i=open("/dev/null",O_RDWR);
dup(i);dup(i); /* handle standart I/O */
umask(027); /* set newly created file permissions */
chdir(RUNNING_DIR); /* change running directory */
lfp=open(LOCK_FILE,O_RDWR|O_CREAT,0640);if (lfp<0)
return (1); /* can not open */if (lockf(lfp,F_TLOCK,0)<0)
return (1); /* can not lock *//* first instance continues */
sprintf(str,"%d/n",getpid());
write(lfp,str,strlen(str)); /* record pid to lockfile */
signal(SIGCHLD,SIG_IGN); /* ignore child */
signal(SIGTSTP,SIG_IGN); /* ignore tty signals */
signal(SIGHUP,signal_handler); /* catch hangup signal */
signal(SIGTERM,signal_handler); /* catch kill signal */
while( 1 )
{/* do something here */
sleep(30);}return 0;
}