Linux守护服务进程(Daemon service)编程

本文介绍守护进程的基础概念及其创建步骤,包括将进程置于后台、进程独立化、重定向标准IO等关键技术点,并给出完整的示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.简介:守护服务进程指的是在后台运行,起到提供服务的进程。

2.步骤:

1)将进程放入后台:

这里利用了fork为当前进程创建一份拷贝(即子进程),然后令父进程退出后子进程被init进程(系统初始化进程,是所有进程的父进程)接管时会将进程放入后台这个特点。


i=fork();
	if (i/* 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/* can not open */
if (lockf(lfp,F_TLOCK,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 (http://blog.youkuaiyun.com/gnuhpc), warmbupt@gmail.com
*        Company:  IBM CDL
*
* =====================================================================================
*/


#include <stdio.h></stdio.h>
#include <stdlib.h></stdlib.h>
#include string.h>
#include <fcntl.h></fcntl.h>
#include <signal.h></signal.h>
#include <unistd.h></unistd.h>

#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
		return 1; /* fork error */
	if (i>0) 
		return 0; /* parent exits */
	
	/* child (daemon) continues */
	int sid = setsid(); /* obtain a new process group */

	if( sid
	{
		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
		return (1); /* can not open */
	if (lockf(lfp,F_TLOCK,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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值