【Linux开发基础】Linux守护服务进程(Daemon service)编程

作者: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.log
                    To 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 background
		return 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;
}

作者:gnuhpc
出处:http://www.cnblogs.com/gnuhpc/


               作者:gnuhpc
               出处:http://www.cnblogs.com/gnuhpc/
               除非另有声明,本网站采用知识共享“署名 2.5 中国大陆”许可协议授权。


分享到:

原文地址:https://www.cnblogs.com/gnuhpc/p/2811919.html