首页 > 代码库 > 守护进程

守护进程

守护进程不属于任何的终端,所以当需要输出某些信息时,它无法像通常程序那样将信息直接输出到标准输出和标准错误输出中。这就需要某些特殊的机制来处理它的输出。为了解决这个问题,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 系统
以上就是建立一个守护进程的基本步骤。当然,一个实际的守护进程要比这个例子复杂许多,但是万变不离其宗,原理都是相同的。通过上面几步,我们可以正确的建立自己的守护进程。