linux守护进程 编写要点

   守护进程(Daemon)是运行在后台的一种特殊进程,脱离于终端。它在执行过程中产生的信息也不会在终端上显示。守护进程周期性地执行某种任务或等待处理某些事件的发生。Linux的大多数服务器就是用守护进程实现的。
编写要点:(最后有完整示例)
1.屏蔽一些有关终端操作的信号。这是为了防止在守护进程还没有正常运行起来前,受到终端干扰退出或者挂起。代码:
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGHUP, SIG_IGN);
2.在后台运行。为避免挂起控制终端将其放入后台执行。方法是在进程中调用fork使父进程终止,让Daemon在子进程中后台执行。代码:
if( (pid = fork()) > 0)
    exit(0);    //父进程,结束父进程,子进程继续
3.脱离控制终端和进程组。代码:
setsid();  //刚才fork的子进程调用
调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。
4.禁止进程重新打开控制终端。现在,进程已经成为无终端的会话组长,并且该会话没有控制终端,但会话组长可以重新申请打开一个控制终端。所以要让进程不是会话组长,来禁止进程重新打开控制终端。代码:
if( (pid = fork()) > 0)
   exit(0);  //之前的子进程结束,子子进程继续运行
此时的进程组id、会话id是之前子进程的pid,现在程序运行的是子子进程。虽然刚才的会话组长结束了,但是它pid还在,因为进程组、会话的生命期结束消失是在这个组、会话中所有进程都结束时才发生的。
5.关闭打开的文件描述符。进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。代码:
#define MAX_FILENO 1024
for(i = 0; i < MAX_FILENO; ++i)
  close(i);
程序能打开的文件最大个数,在头文件<linux/limits.h>中 NR_OPEN 指定,查看并使用此值。
6.改变当前工作目录。进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。写运行日志的进程将工作目录改变到特定目录如/tmp。代码:
chdir("/");
7.重设文件创建掩码。进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除。代码:
umask(0);
8.处理SIGCHLD信号。对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN来解决问题。代码:
signal(SIGCHLD, SIG_IGN);
上面说了这么多都是守护进程的编写要点,值得一提的是系统已经给我们提供了API让进程直接成为守护进程:

#include <unistd.h>

int daemon(int nochdir, int noclose);  //使进程成为守护进程

//0,则改变工作目录到“/”; 0,则重定向stdin、stdout、stderr到“/dev/null”

此外,守护进程可以与日志守护进程建立联系,将日志信息写入日志文件。用到的函数:
#include <syslog.h>
void openlog(const char *ident, int option, int facility);
//打开当前程序与日志守护进程之间的联系。
ident,是一个标记,ident所表示的字符串将固定地加在每行日志的前面以标识这个日志,通常就写成当前程序的名称以作标记。
option,一般是下列选项值取“与”运算(使用“|”表示,如“LOG_CONS | LOG_PID”)的结果:
  LOG_CONS:如果送到system logger时发生问题,直接写入系统终端;
  LOG_NDELAY:立即开启连接,通常连接是在第一次写入消息时才打开的;
    LOG_NOWAIT: Don't wait for child processes that may have  been  created while logging the message.  
               (The GNU C library does not create a child process, so this option has no effect on Linux.)
    LOG_ODELAY:The converse of LOG_NDELAY; opening of the connection is delayed until syslog() is called.
               (This is the default,and need not be specified.)
  LOG_PERROR:将消息也同时送到stderr设备;
  LOG_PID:将进程PID含入所有消息中。
facility,指明记录日志的程序的类型,它主要具有如下几类日志类型:
  LOG_AUTH :安全/授权消息
  LOG_AUTHPRIV:安全/授权消息(私有)
  LOG_CRON:时间守护进程(cron和at)专用
  LOG_DAEMON:其它系统守护进程
  LOG_KERN:核心消息
  LOG_LOCAL0到LOG_LOCAL7:系统保留
  LOG_LPR:printer子系统
  LOG_MAIL:mail子系统
  LOG_NEWS:USENET新闻子系统
  LOG_SYSLOG:syslogd进程内部所产生的消息
  LOG_USER(缺省):一般使用者缺省使用消息
  LOG_UUCP:UUCP子系统
  LOG_FTP:FTP子系统使用
void syslog(int priority, const char *format, ...);
//产生一条日志信息,然后由日志守护程序将其发送到各日志文件。
level priority,紧急级别:
    LOG_EMERG:紧急状况
  LOG_ALERT:高优先级问题,比如说数据库崩溃等,必须要立即采取反应行动
  LOG_CRIT:重要状况发生,比如硬件故障
  LOG_ERR:错误发生
  LOG_WARNING:警告发生
  LOG_NOTICE:一般状况,需要引起注意
  LOG_INFO:信息状况
  LOG_DEBUG:调试消息
第二个参数是消息及其格式,之后是格式对应的参数,如同C语言里面printf输出函数一样使用。
  在实际使用中,如果我们的程序要使用系统日志功能,只需要在程序启动时使用openlog函数来连接syslogd程序,后面随时用syslog函数写日志就行了。
void closelog(void);
//关闭日志。虽然这个函数简单,但必不可少,因为日志也是资源,只打开不管可能会造成内存不足。
守护程序完整示例:
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syslog.h>
#include <sys/param.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX_FILENO 1024
int init_daemon()
{
	int pid;
	int i;
	signal(SIGTTOU,SIG_IGN);
	signal(SIGTTIN,SIG_IGN);
	signal(SIGTSTP,SIG_IGN);
	signal(SIGHUP ,SIG_IGN);
	if( (pid = fork()) > 0)
	{
		exit(EXIT_SUCCESS);
	}
	else if(pid< 0)
	{
		perror("fork");
		exit(EXIT_FAILURE);
	}
	setsid();
	if( (pid = fork()) > 0)
	{
		exit(EXIT_SUCCESS);
	}
	else if(pid< 0)
	{
		perror("fork");
		exit(EXIT_FAILURE);
	}
	for(i = 0; i < MAX_FILENO; ++i)
	{
		close(i);
	}
	open("/dev/null", O_RDONLY);
	open("/dev/null", O_RDWR);
	open("/dev/null", O_RDWR);
	chdir("/tmp");
	umask(0);
	signal(SIGCHLD,SIG_IGN);
	return 0;
}
int main(int argc,char *argv[])
{
	init_daemon();
	openlog("MyLog", LOG_PID, LOG_KERN);
	while(1)
	{
		sleep(1);
		syslog(LOG_INFO,"This is my log : %s\n",argv[0]);
	}
}
此时,ps -axj,显示:          
PPID   PID  PGID   SID  TTY   TPGID STAT   UID   TIME  COMMAND
 1    4613  4612   4612  ?     -1    S     1000   0:00  ./main
   
   
打开/var/log/messages 文件:
MyLog[4613]: This is my log : ./main
 
 
 
 

                
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值