守护进程(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的子进程调用
MyLog[4613]: This is my log : ./main调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。4.禁止进程重新打开控制终端。现在,进程已经成为无终端的会话组长,并且该会话没有控制终端,但会话组长可以重新申请打开一个控制终端。所以要让进程不是会话组长,来禁止进程重新打开控制终端。代码:if( (pid = fork()) > 0)
exit(0); //之前的子进程结束,子子进程继续运行
此时的进程组id、会话id是之前子进程的pid,现在程序运行的是子子进程。虽然刚才的会话组长结束了,但是它pid还在,因为进程组、会话的生命期结束消失是在这个组、会话中所有进程都结束时才发生的。
5.关闭打开的文件描述符。进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。代码:#define MAX_FILENO 1024for(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 COMMAND1 4613 4612 4612 ? -1 S 1000 0:00 ./main打开/var/log/messages 文件: