守护进程不属于不论什么的终端,所以当须要输出某些信息时,它无法像通常程序那样将信息直接输出到标准输出和标准错误输出中。这就须要某些特殊的机制来处理它的输出。为了解决问题,Linux 系统提供了syslog()系统调用。通过它,守护进程能够向系统的log 文件写入信息。它在Linux 系统函数库syslog.h 中的定义例如以下:
void syslog( int priority, char *format, ...);
该调用有两个參数:
priority 參数指明了进程要写入信息的等级和用途,能够的取值如表3-2 所看到的:
priority 等级取值及其含义等级值描写叙述
LOG_EMERG 0 系统崩溃(最高优先级)
LOG_ALERT 1 必须马上处理的动作
LOG_CRIT 2 危险的情况
LOG_ERR 3 错误
LOG_WARNING 4 警告
LOG_NOTICE 5 正常可是值得注意的情况(缺省)
LOG_INFO 6 信息
LOG_DEBUG 7 调试信息(最低优先级)
假设等级没有被指定,就自己主动取缺省值LOG_NOTICE。
priority 用途的取值及其含义用途描写叙述
LOG_AUTH 安全/管理信息
LOG_AUTHPRIV 安全/管理信息(私人)
LOG_CRON cron 守护进程
LOG_DAEMON 系统守护进程
LOG_FTP ftp 守护进程
LOG_KERN 内核守护进程
LOG_LOCAL0 local use
LOG_LOCAL1 local use
LOG_LOCAL2 local use
LOG_LOCAL3 local use
LOG_LOCAL4 local use
LOG_LOCAL5 local use
LOG_LOCAL6 local use
LOG_LOCAL7 local use
LOG_LPR 行打印机系统
LOG_MAIL mail 系统
LOG_NEWS network news 系统
LOG_SYSLOG syslogd 进程产生的信息
LOG_USER 随机用户信息(缺省)
LOG_UUCP UUCP 系统
假设没有指定用途,缺省的LOG_USER 就自己主动被指定。
syslog()调用后面的參数使用方法和printf()类似,message 是一个格式串,指定了记录输出的格式。须要注意的是在这个串的最后须要指定一个%m,其相应着errno 错误码。以下是一个样例:
syslog(LOG_INFO|LOG_LOCAL2,”rename(%s,%s): %m”,file1,file2);
在/etc/syslog.conf 中指定了各种信息存放的位置。比如,在syslog.conf 中以下的一项:
local7.debug /var/log/temp/log
表示系统将全部LOG_DEBUG|LOG_LOCAL7 的信息都储存到/var/log/temp/log 中,这
样能够方便信息的分类整理。
在一个进程使用syslog()的时候,应该先使用openlog()打开系统记录:
#include <syslog.h>
void openlog(const char *ident, int options, int facility);
參数ident 是一个字符串,它将被加在全部用syslog()写入的信息前。通常这个參数是
程序的名字。
參数 option 的取值及其含义
參数描写叙述
LOG_CONS 假设不能写入log 信息,则直接将其发往主控台
LOG_NDELAY 直接建立与syslogd 进程的连接而不是打开log 文件
LOG_PERROR 将信息写入log 的的同一时候也发送到标准错误输出
LOG_PID 在每一个信息中增加pid 信息。
參数facility 指定了syslog()调用的缺省用途值。
在使用完log 之后,能够使用系统调用closelog()来关闭它:
void closelog(void);
守护进程的建立
在介绍守护进程的建立之前,首先来看一下以下的这个例程daemon_init() ,它演示了
建立一个守护进程的所有过程:
#include <sys/types.h> #include <signal.h> #include <unistd.h> #include <syslog.h> #define MAXFD 64 void daemon_init(const char *pname, int facility) { int i: pid_t pid; /* fork,终止父进程 */ if (pid=fork()) exit(0); /* 第一子进程 */ setsid(); signal(SIGHUP,SIG_IGN); /* fork,终止第一子进程 */ if (pid=fork()) exit(0); /* 第二子进程 */ daemon_proc=1; /* 将工作文件夹设定为"/" */ chdir("/"); /* 清除文件掩码 */ umask(0); /* 关闭全部文件句柄 */ for (i=0;i<MAXFD;i++) { close(i); } /* 打开log */ openlog(pname,LOG_PID,facility); }
看过了上面的程序,以下我们就来讲讲建立一个守护进程须要进行哪些操作:
1.fork
首先须要fork 一个子进程并将父进程关闭。假设进程是作为一个shell 命令在命令行上
前台启动的,当父进程终止时,shell 就觉得该命令已经结束。这样子进程就自己主动称为了后
台进程。并且,子进程从父进程那里继承了组标识符同一时候又拥有了自己的进程标识符,这
样保证了子进程不会是一个进程组的首进程。这一点是下一步setsid 所必须的。
2.setsid
setsid()调用创建了一个新的进程组,调用进程成为了该进程组的首进程。这样,就使该进程脱离了原来的终端,成为了独立于终端外的进程。
3.忽略SIGHUP 信号,又一次fork
这样使进程不在是进程组的首进程,能够防止在某些情况下进程意外的打开终端而又一次与终端发生联系。
4.改变工作文件夹,清除文件掩码
改变工作文件夹主要是为了切断进程与原有文件系统的联系。而且保证不管从什么地方启动进程都能正常的工作。清除文件掩码是为了消除进程自身掩码对其创建文件的影响。
5.关闭所有已打开的文件句柄
这是为了防止子进程继承了在父进程中打开的文件而使这些文件始终保持打开从而产生某些冲突。
6.打开log 系统
以上就是建立一个守护进程的基本步骤。当然,一个实际的守护进程要比这个样例复杂很多,可是万变不离其宗,原理都是同样的。通过上面几步,我们能够正确的建立自己的守护进程。