APUE 守护进程

《UNIX环境高级编程》第13章 守护进程 笔记

守护进程(daemon)是生存期长的一种进程。它们常常在系统引导装入时启动,仅在系统关闭时才终止。因为它们没有控制终端,所以说它们是在后台运行的。UNIX系统有很多守护进程,它们执行日常事务活动。

守护进程的特征

系统进程依赖于操作系统实现。父进程ID为0的各进程通常是内核进程,它们作为系统引导装入过程的一部分而启动。(init是个例外,它是一个由内核在引导装入时启动的用户层次的命令。)内核进程是特殊的,通常存在于系统的整个生命期中。它们以超级用户特权运行,无控制终端,无命令行。

对于需要在进程上下文执行工作但却不被用户层进程上下文调用的每一个内核组件,通常有它自己的内核守护进程。例如,在Linux中

  • kswapd守护进程也称为内存换页守护进程。它支持虚拟内存子系统在经过一段时间后将脏页面慢慢地写回磁盘来回收这些页面
  • f1ush守护进程在可用内存达到设置的最小阅值时将脏页面冲洗至磁盘。它也定期地将脏页面冲洗回磁盘来减少在系统出现故障时发生的数据丢失。多个冲洗守护进程可以同时存在,每个写回的设备都有一个冲洗守护进程。
  • sync_supers守护进程定期将文件系统元数据冲洗至磁盘。
  • jbd守护进程帮助实现了ext4文件系统中的日志功能。
  • cron守护进程在定期安排的日期和时间执行命令。许多系统管理任务是通过cron每隔一段固定的时间就运行相关程序而得以实现的。
  • atd守护进程与cron类似,它允许用户在指定的时间执行任务,但是每个任务它只执行一次,而非在定期安排的时间反复执行。
  • cupsa守护进程是个打印假脱机进程,它处理对系统提出的各个打印请求。
  • sshd守护进程提供了安全的远程登录和执行设施注意,大多数守护进程都以超级用户(root)特权运行。所有的守护进程都没有控制终端其终端名设置为问号。

内核守护进程以无控制终端方式启动。用户层守护进程缺少控制终端可能是守护进程调用了 setid的结果。

编程规则

在编写守护进程程序时需遵循一些基本规则,以防止产生不必要的交互作用。下面先说明这些规则,

  1. 首先要做的是调用umask将文件模式创建屏蔽字设置为一个已知值(通常是0)。由继承得来的文件模式创建屏蔽字可能会被设置为拒绝某些权限。如果守护进程要创建文件,那么它可能要设置特定的权限。
  2. 调用fork,然后使父进程exit。这样做实现了下面几点。第一,如果该守护进程是作为一条简单的shell命令启动的,那么父进程终止会让shell认为这条命令已经执行完毕。第二,虽然子进程继承了父进程的进程组ID,但获得了一个新的进程ID,这就保证了子进程不是一个进程组的组长进程。这是下面将要进行的setid调用的先决条件。
  3. 调用setid创建一个新会话,执行3步:成为新会话的首进程、成为新进程组的组长进程、没有控制终端
  4. 将当前工作目录更改为根目录
  5. 关闭不再需要的文件描述符
  6. 某些守护进程打开/dev/null使其具有文件描述符0、1和2,这样,任何一个试图读标准输入、写标准输出或标准错误的库例程都不会产生任何效果。因为守护进程并不与终端设备相关联所以其输出无处显示,也无处从交互式用户那里接收输入。

出错记录

守护进程存在的一个问题是如何处理出错消息。因为它本就不应该有控制终端,所以不能只是简单地写到标准错误上。我们不希望所有守护进程都写到控制台设备上,因为在很多工作站上控制台设备都运行着一个窗口系统。我们也不希望每个守护进程将它自己的出错消息写到一个单独的文件中。对任何一个系统管理人员而言,如果要关心哪一个守护进程写到哪一个记录文件中并定期地检查这些文件,那么一定会使他感到头痛。所以,需要有一个集中的守护进程出错记录设施。

BSD的sys1og设施得到了广泛的应用。大多数守护进程都使用这一设施

有下面3种产生日志消息的方法:

  1. 内核例程可以调用log函数
  2. 大多数用户进程(守护进程)调用sys1og(3)函数来产生日志消息
  3. 无论一个用户进程是在此主机上,还是在通过TCP/IP网络连接到此主机的其他主机上,都可将日志消息发向UDP端口514。

通常, syslogd守护进程读取所有3种格式的日志消息。此守护进程在启动时读一个配置文件,其文件名一般为/etc/sys1og.conf,该文件决定了不同种类的消息应送向何处。例如,紧急消息可发送至系统管理员(若已登录),并在控制台上打印,而警告消息则可记录到一个文件中。

该设施的接口是:

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

单实例守护进程

为了正常运作,某些守护进程会实现为,在任一时刻只运行该守护进程的一个副本。例如,这种守护进程可能需要排它地访问一个设备。对cron守护进程而言,如果同时有多个实例运行,那么每个副本都可能试图开始某个预定的操作,于是造成该操作的重复执行,这很可能导致出错。

文件和记录锁机制为一种方法提供了基础,该方法保证一个守护进程只有一个副本在运行。

守护进程的惯例

在UNIX系统中,守护进程遵循下列通用惯例:

  1. 若守护进程使用锁文件,那么该文件通常存储在/var/run目录中
  2. 若守护进程支持配置选项,那么配置文件通常存放在/etc目录中
  3. 守护进程可用命令行启动,但通常它们是由系统初始化脚本之一(/etc/rc*etc/init.d/*)启动的。如果在守护进程终止时,应当自动地重新启动它,则我们可在/etc/inittab中为该守护进程包括 respawn记录项,这样,init就将重新启动该守护进程。
  4. 若一个守护进程有一个配置文件,那么当该守护进程启动时会读该文件,但在此之后般就不会再查看它。若某个管理员更改了配置文件,那么该守护进程可能需要被停止,然后再启动,以使配置文件的更改生效。