UNIX环境高级编程(13-守护进程)

主要特点

  • 一般在系统启动时装入,仅在系统关闭时终止。
  • 大多数守护进程以超级用户特权运行。
  • 所有的守护进程都没有控制终端,其终端名设置为问号。
    • 内核守护进程以无控制终端方式启动。
    • 用户层守护进程可以通过调用setsid实现。
  • 用户层守护进程的父进程是init进程。

消息输出

前面提到,守护进程是没有控制终端的,显然无法将自己的消息输出到标准输出或标准错误上。而且系统中运行着许多守护进程,因此需要一个集中的守护进程记录设施,即syslog

如上图所示,主要有3中产生日志消息的方式:

  1. 内核例程调用log函数
  2. 大多数用户进程调用syslog函数
  3. 将日志消息发送到UDP的514端口

而syslogd守护进程接收这些日志消息,在其启动前会读取配置文件(/etc/syslog.conf),以决定各类消息的处理方式。

#include <syslog.h>
void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void);
// Returns: previous log priority mask value
int setlogmask(int maskpri);

注:在没有调用openlog的情况下,先调用了syslog,会自动调用openlog

ident参数指向的字符串会被加到日志消息中去,因此一般指定为程序名称。

option参数指定各种选项的位屏蔽,选项见图13.3。

facility参数可选值见图13.4。

priority参数包含facility和level(图13.5)的组合,如果参数中没有指定facility,则会使用openlog中指定的facility,如果没有调用openlog,那么会使用默认值LOG_USER

编程规则

可以按照如下步骤编写一个守护进程。

  1. 调用umask函数将文件模式创建屏蔽字设置为指定值(通常为0)。守护进程可能需要创建一些文件,如果使用继承的屏蔽字,可能文件的权限会不符合预期。
  2. 调用fork后,使父进程exit。这样可以保证子进程不是进程组的组长进程。
  3. 调用setsid创建新会话。这可以保证当前进程没有控制终端,且成为新会话的首进程和新进程组的组长进程。
  4. 将当前工作目录改为根目录或某个指定位置。
  5. 关闭不再需要的文件描述符。可以使用getrlimit函数获取最高文件描述符值,并关闭直到该值的所有描述符。
  6. 某些守护进程将文件描述符0、1和2指向/dev/null,这样任何需要输入输出的库例程都不会产生影响。

单实例守护进程

某些守护进程在同一时刻只能运行一个实例程序,这时候可以使用文件和记录锁(下文简称文件锁,详见14章)来实现这个功能。

守护进程只要创建一个固定名字的文件(一般在/var/run目录中),并在该文件整体上加一把写锁,那么此后其他的守护进程如果想要给该文件加锁就会失败,也就不应该继续运行。在守护进程终止时,锁会被自动删除,简化了复原过程。

posted @ 2021-01-08 10:44  maxiaowei0216  阅读(64)  评论(0编辑  收藏  举报