第 37 章 守护进程 (Daemons)

      +

      核心结论

      • daemon 是长寿、无控制终端的后台进程:cron/sshd/httpd/inetd 等;通常名字以 d 结尾;常以特权(root)运行——需按第 38 章准则编写;内核线程(如 pdflush)ps 显示在 [] 中。

      • daemon 化 7 步流程:fork+setsid(脱离控制终端)+ 二次 fork(防再获控制终端)+ umask(0) + chdir(/) + close 继承 fd + reopen stdin/stdout/stderr 到 /dev/null。

      • becomeDaemon() 库函数封装 7 步:flags 位掩码可选跳过某些步骤(BD_NO_CHDIR/BD_NO_CLOSE_FILES/BD_NO_REOPEN_STD_FDS/BD_NO_UMASK0);glibc daemon() 函数无 flags 灵活性。

      • daemon 编写准则:daemon 长寿——严防内存泄漏/fd 泄漏;只允许单实例运行(§55.6);SIGTERM 优雅关闭(init 5 秒后发 SIGKILL);SIGHUP 重读配置 + 重开日志文件。

      • SIGHUP 重读配置模式:daemon 无控制终端 → kernel 不自动发 SIGHUP;可作「重读配置」信号;handler 设标志位 + 主循环检查 + 重读 conf + 重开 log;logrotate(8) 自动触发 SIGHUP。

      • syslog API 三函数:openlog (建立连接 + 设置默认) + syslog (写消息) + closelog (关闭);setlogmask 过滤 level;LOG_* 常量(facility + level);%m 替换 errno 字符串。

      • syslog 架构:syslogd 守护从 /dev/log (Unix domain) + UDP 514 收消息;按 /etc/syslog.conf 分发到 terminal/file/FIFO/user/remote syslogd;klogd 从 /proc/kmsg + syslog(2) 收内核消息转发到 syslogd。

      本章主旨

      本章深入 daemon 化流程与日志记录——从传统的「双重 fork + setsid」7 步,到 syslog 集中日志架构。读者应掌握:daemon 的定义与特征、becomeDaemon 实现、信号处理(SIGTERM 优雅关闭 + SIGHUP 重读配置)、syslog API(openlog/syslog/closelog + setlogmask)、syslog 架构(syslogd + /dev/log + syslog.conf)、format-string 攻击防御。理解「为什么二次 fork」是脱离控制终端的关键;理解「SIGHUP 复用为重读配置信号」是 daemon 设计的常见模式。写网络服务器、监控系统、批处理守护进程都依赖这些知识。

      一、核心概念

      本章围绕 6 个核心概念展开:daemon 特征与示例、daemon 化 7 步流程、becomeDaemon 实现、daemon 编写准则(信号 + 单实例)、SIGHUP 重读配置、syslog API 与架构。

      概念 定义 + 重要性 实现提示

      daemon 特征

      长寿 + 无控制终端 + 后台运行;避免终端信号(SIGINT/SIGTSTP/SIGHUP);命名约定以 d 结尾;常以 root 运行;内核线程在 ps 显示 [] 中;典型:cron/sshd/httpd/inetd。

      §37.1;无控制终端确保 kernel 不自动发 job-control/终端信号;可由 init 在启动时启动;运行到系统关机。

      daemon 化 7 步

      (1) fork 后父 exit(脱离 shell + 子非进程组 leader);(2) setsid 创建新会话(脱离控制终端);(3) 二次 fork(确保非 session leader,System V 约定防再获控制终端);(4) umask(0);(5) chdir("/");(6) close 继承 fd;(7) reopen stdin/stdout/stderr 到 /dev/null。

      §37.2;BSD 风格可省略二次 fork(BSD 需 TIOCSCTTY 才获控制终端);close from n 在 Linux 不可用;用 sysconf(_SC_OPEN_MAX) 或 BD_MAX_CLOSE=8192 作 fallback。

      becomeDaemon() 实现

      TLPI 提供的 7 步封装函数;flags 位掩码可选跳过某些步骤(BD_NO_CHDIR=01 / BD_NO_CLOSE_FILES=02 / BD_NO_REOPEN_STD_FDS=04 / BD_NO_UMASK0=010);glibc daemon() 函数无 flags。

      §37.2,Listing 37-1/37-2;二次 fork 后子不是 session leader;close 0..maxfd 后 reopen 0/1/2 到 /dev/null;典型用法:becomeDaemon(0) + 业务逻辑。

      daemon 编写准则

      严防内存泄漏 + fd 泄漏(kill+restart 才能恢复);只允许单实例(§55.6 文件锁/PID 文件);SIGTERM 优雅关闭(init 关机信号,5 秒后 SIGKILL);SIGHUP 重读配置 + 重开日志;标准 log/config 路径(/etc / /var/log)。

      §37.3-37.4;长生命周期 = bug 影响时间长;handler 必须快速清理;init 关机给 5 秒(不是 5 秒 CPU)。

      SIGHUP 重读配置模式

      daemon 无控制终端 → kernel 不发 SIGHUP;可作「重读配置」信号;handler 设 hupReceived 标志;主循环检查标志 + logClose + logOpen + readConfigFile;logrotate(8) 发 SIGHUP 触发;典型 daemon 都支持。

      §37.4,Listing 37-3 daemon_SIGHUP.c;hupReceived = volatile sig_atomic_t;logMessage/logClose/logOpen/readConfigFile 辅助函数;用 killall -HUP daemon 触发。

      syslog API 与架构

      openlog (建立连接 + 默认 facility + LOG_PID 等选项) + syslog (priority + format + args) + closelog + setlogmask;syslogd 从 /dev/log + UDP 514 收消息;按 /etc/syslog.conf 分发;klogd 转发内核消息。

      §37.5;LOG_CONS / LOG_NDELAY / LOG_PID / LOG_PERROR 等选项;facility (LOG_USER/AUTH/DAEMON/MAIL 等) + level (LOG_EMERG 到 LOG_DEBUG);%m 替换 errno;format-string 攻击防御(用 "%s")。

      二、详细笔记

      37.1 daemon 概述

      What:daemon 是长寿、无控制终端的后台进程;用于 cron/sshd/httpd/inetd 等系统服务。

      Why:理解 daemon 特征是设计长生命周期后台服务的前提——避免终端信号干扰、可独立于用户会话运行。

      How

      特征(§37.1):

      • 长寿:系统启动时创建,运行到系统关机。

      • 无控制终端:kernel 不自动发 job-control/终端信号(SIGINT/SIGTSTP/SIGHUP)。

      • 后台运行:不与用户交互。

      • 命名约定:以 d 结尾(cron/sshd/httpd);非通用规则。

      典型 daemon:

      • cron:定时执行命令。

      • sshd:远程登录。

      • httpd:Web 服务器(Apache/Nginx)。

      • inetd:超级服务器,监听端口启动对应服务。

      特权与编写准则:

      • 多数标准 daemon 以 root 运行——按第 38 章准则编写。

      • 内核线程(如 pdflush):ps 显示 [] 中,代码是内核一部分。

      • init 启动 daemon;典型 /etc/init.d 或 systemd unit。

      When:写系统服务、后台监控、批处理;避免把交互式程序当 daemon 写。

      Example

      # 查看 daemon 进程(Linux)
      $ ps -eo 'pid ppid pgid sid tty cmd' | grep -E 'd\s' | head -5

      37.2 创建 daemon(7 步流程)

      What:将进程转变为 daemon 的标准 7 步流程——fork + setsid + 二次 fork + umask + chdir + close + reopen /dev/null。

      Why:理解每一步的目的(脱离 shell/控制终端 + 防再获控制终端 + 文件系统清理 + fd 隔离)是写 daemon 的基础。

      How

      7 步详解(§37.2):

      1. fork + 父 exit:父终止后 shell 显示新提示;子继承父的 PGID 但有自己的 PID(确保子不是进程组 leader)。

      2. setsid:调用者成为新会话 leader;脱离控制终端;新会话无控制终端。

      3. 二次 fork + 父 exit:System V 约定——非 session leader 永远不能获控制终端;BSD 风格可省略(BSD 需 TIOCSCTTY 才获);冗余 fork 无害。

      4. umask(0):确保创建文件/目录时权限是请求值(不受父 shell mask 影响)。

      5. chdir("/"):确保 cwd 在 root——可卸载 cwd 所在文件系统;典型 daemon 用 / 或配置目录(如 /var/spool/cron)。

      6. close 所有 fd:避免 fd 泄漏;fd 0/1/2 通常指向终端——关闭;fd 是有限资源。

      7. reopen stdin/stdout/stderr 到 /dev/null:库函数 stdio 调用不会失败;防止 daemon 后续 open fd 1/2 误用为 stdout/stderr;/dev/null 写丢弃、读 EOF。

      becomeDaemon 实现(§37.2,Listing 37-2):

      // 摘自《The Linux Programming Interface》第 37 章 — Listing 37-2
      // 摘自 daemons/become_daemon.c
      int becomeDaemon(int flags) {
          int maxfd, fd;
          /* Step 1: fork + parent exit */
          switch (fork()) {
          case -1: return -1;
          case 0: break;                  /* Child falls through */
          default: _exit(EXIT_SUCCESS);   /* Parent exits */
          }
          /* Step 2: setsid */
          if (setsid() == -1) return -1;
          /* Step 3: second fork */
          switch (fork()) {
          case -1: return -1;
          case 0: break;
          default: _exit(EXIT_SUCCESS);
          }
          /* Step 4-7 based on flags */
          if (!(flags & BD_NO_UMASK0)) umask(0);
          if (!(flags & BD_NO_CHDIR)) chdir("/");
          if (!(flags & BD_NO_CLOSE_FILES)) {
              maxfd = sysconf(_SC_OPEN_MAX);
              if (maxfd == -1) maxfd = BD_MAX_CLOSE;  /* 8192 fallback */
              for (fd = 0; fd < maxfd; fd++) close(fd);
          }
          if (!(flags & BD_NO_REOPEN_STD_FDS)) {
              close(STDIN_FILENO);
              fd = open("/dev/null", O_RDWR);    /* 应该 == 0 */
              if (fd != STDIN_FILENO) return -1;
              dup2(STDIN_FILENO, STDOUT_FILENO);
              dup2(STDIN_FILENO, STDERR_FILENO);
          }
          return 0;
      }

      flags(§37.2,Listing 37-1):

      • BD_NO_CHDIR = 01:跳过 chdir("/")。

      • BD_NO_CLOSE_FILES = 02:跳过 close 继承 fd。

      • BD_NO_REOPEN_STD_FDS = 04:不重开 0/1/2 到 /dev/null。

      • BD_NO_UMASK0 = 010:不 umask(0)。

      • BD_MAX_CLOSE = 8192:sysconf 不可知时的 fallback max fd。

      注意:

      • glibc daemon(int nochdir, int noclose) 函数无 flags 灵活性。

      • Solaris 9 / 部分 BSD 提供 closefrom(n)——Linux 不可用。

      • close 0..maxfd 会浪费 close() 调用——但简单可靠。

      • 二次 fork 后子进程 PID ≠ session ID——非 session leader。

      When:任何后台进程都需要 daemon 化(避免终端信号、避免持有文件系统);典型:服务器、监控、定时任务。

      Example:见上述 becomeDaemon 实现。

      37.3 daemon 编写准则

      What:daemon 长寿——严防资源泄漏;只允许单实例;SIGTERM 优雅关闭。

      Why:理解 daemon 的特殊需求是写可靠长生命周期服务的前提。

      How

      资源泄漏防御(§37.3):

      • 内存泄漏(§7.1.3)——只有 kill + restart 才能恢复;长期累积 → OOM。

      • fd 泄漏——长期累积 → EMFILE;持有已删除文件 → 不可释放磁盘空间。

      • 唯一补救:kill + restart(修复 bug 后)。

      单实例运行(§37.3):

      • 典型需求:cron 一个实例、sshd 一个守护。

      • 实现:§55.6 文件锁 / PID 文件 / 命名管道 / fcntl F_SETLK。

      • PID 文件惯例:/var/run/<daemon>.pid——含进程 PID;启动时检查已存在则拒绝启动。

      SIGTERM 处理(§37.3):

      • init 关机时给所有子进程发 SIGTERM(默认终止)。

      • 5 秒后 init 发 SIGKILL(不可 catch)。

      • daemon 应 SIGTERM handler 快速清理:flush log、close fd、unlink PID 文件、保存状态。

      • 注意:5 秒是 wall clock 时间,不是 CPU 时间——所有 daemon 同时清理,CPU 竞争。

      When:写所有 daemon 都需遵循;尤其生产环境。

      Example

      // 摘自《The Linux Programming Interface》第 37 章
      static volatile sig_atomic_t terminate = 0;
      static void termHandler(int sig) { terminate = 1; }
      int main() {
          signal(SIGTERM, termHandler);
          /* daemon 业务逻辑 */
          while (!terminate) {
              /* 工作单元 */
          }
          /* 清理:flush log / close fd / unlink PID file */
          cleanup();
          return 0;
      }

      37.4 SIGHUP 重读配置模式

      What:daemon 用 SIGHUP 作为「重读配置 + 重开日志」信号;handler 设标志,主循环检查 + 处理。

      Why:daemon 无控制终端 → kernel 不发 SIGHUP;可安全用作应用信号;提供「on-the-fly 配置更新」能力。

      How

      为什么 SIGHUP(§37.4):

      • daemon 无控制终端 → 终端断开不发 SIGHUP。

      • SIGHUP 在普通进程是「终端断开」语义;daemon 复用为「重读配置」。

      • logrotate(8) 等工具发 SIGHUP 触发日志轮转。

      实现模式(§37.4,Listing 37-3 daemon_SIGHUP.c):

      // 摘自《The Linux Programming Interface》第 37 章 — Listing 37-3
      // 摘自 daemons/daemon_SIGHUP.c
      static volatile sig_atomic_t hupReceived = 0;
      static void sighupHandler(int sig) {
          hupReceived = 1;   /* 简洁 — handler 内只设标志 */
      }
      int main(int argc, char *argv[]) {
          const int SLEEP_TIME = 15;
          int count = 0, unslept;
          struct sigaction sa;
          sigemptyset(&sa.sa_mask);
          sa.sa_flags = SA_RESTART;
          sa.sa_handler = sighupHandler;
          sigaction(SIGHUP, &sa, NULL);
          becomeDaemon(0);
          logOpen(LOG_FILE);
          readConfigFile(CONFIG_FILE);
          unslept = SLEEP_TIME;
          for (;;) {
              unslept = sleep(unslept);    /* Returns > 0 if interrupted */
              if (hupReceived) {
                  logClose();
                  logOpen(LOG_FILE);
                  readConfigFile(CONFIG_FILE);
                  hupReceived = 0;
              }
              if (unslept == 0) {
                  count++;
                  logMessage("Main: %d", count);
                  unslept = SLEEP_TIME;
              }
          }
      }

      要点:

      • hupReceivedvolatile sig_atomic_t——async-safe 访问。

      • handler 只设标志——避免 handler 内做复杂操作(可能异步信号不安全)。

      • 主循环用 sleep 自动处理 EINTR(unslept > 0 表示未睡够)。

      • 主循环每次 wake 检查 hupReceived——避免 SIGCONT/SIGHUP 错过。

      • logOpen/logClose/reopen——配合 logrotate mv + SIGHUP 实现 log rotation。

      • readConfigFile 每次 SIGHUP 重读——on-the-fly 配置更新。

      替代方案:

      • 部分 daemon 在 SIGHUP handler 内 close all fd + exec()——重启自身。

      • 不推荐:丢失全局状态;复杂。

      When:所有需要「on-the-fly 配置」的 daemon——logrotate 重开 log、配置变更生效。

      Example:见上述 daemon_SIGHUP.c。

      37.5 syslog API

      Whatopenlog/syslog/closelog/setlogmask 三函数 + 过滤;syslogd 守护处理消息分发。

      Why:syslog 提供集中日志管理——避免每个 daemon 自己管日志;统一到 /var/log;支持远程日志聚合。

      How

      API(§37.5.2):

      // 摘自《The Linux Programming Interface》第 37 章
      #include <syslog.h>
      void openlog(const char *ident, int log_options, int facility);
      void syslog(int priority, const char *format, ...);
      void closelog(void);
      int setlogmask(int mask_priority);

      openlog(§37.5.2):

      • ident:每条消息前缀(通常程序名);openlog 仅拷贝指针——应用须保证字符串不被修改。

      • log_options 位掩码:LOG_CONS(失败写 console)、LOG_NDELAY(立即打开 /dev/log)、LOG_NOWAIT(不 wait 子进程——Linux 无效)、LOG_ODELAY(延迟打开,默认)、LOG_PERROR(同时写 stderr)、LOG_PID(消息含 PID)。

      • facility:默认 facility 值(表 37-1)。

      syslog(§37.5.2):

      • priority:facility | level;facility 缺省用 openlog 设置或 LOG_USER;level(表 37-2,从 LOG_EMERG 到 LOG_DEBUG)。

      • format:同 printf——可含 %m 替换 errno 字符串。

      • 安全:用户输入必须用 "%s" 包裹——防 format-string 攻击。

      • 示例:

      openlog(argv[0], LOG_PID | LOG_CONS, LOG_LOCAL0);
      syslog(LOG_ERROR, "Bad argument: %s", argv[1]);   /* facility 默认 LOG_LOCAL0 */
      syslog(LOG_USER | LOG_INFO, "Exiting");            /* 显式 facility */

      setlogmask(§37.5.2):

      • 过滤 level——不 mask 的 level 消息丢弃。

      • LOG_MASK(level) 转 bit;LOG_UPTO(level) 包括 level 及以上。

      • 示例:setlogmask(LOG_UPTO(LOG_ERR)) 仅记录 ERR 及以上。

      facility 表(表 37-1):LOG_AUTH/AUTHPRIV/CRON/DAEMON/FTP/KERN/LOCAL0-7/LPR/MAIL/NEWS/SYSLOG/USER/UUCP。

      level 表(表 37-2):LOG_EMERG/ALERT/CRIT/ERR/WARNING/NOTICE/INFO/DEBUG。

      closelog(§37.5.2):

      • 关闭 /dev/log socket fd。

      • daemon 通常不调——保持连接。

      syslog 架构(§37.5.1):

      • syslogd:从 /dev/log(Unix domain datagram)+ UDP 514 收消息。

      • 按 /etc/syslog.conf 分发到 terminal/file/FIFO/user/remote。

      • klogd:从 /proc/kmsg 或 syslog(2) 收内核消息转发到 syslogd。

      • syslog(2) 系统调用(glibc klogctl 包装)读内核 ring buffer。

      • 注意区分 syslog(2) vs syslog(3)——前者内核消息,后者用户程序 API。

      syslog.conf 规则(§37.5.3):

      • 格式:facility.level action

      • facility.level 用 . 分隔;* 通配;; 多 selector;level=none 排除。

      • action:文件路径、@remote、user、FIFO、设备、-prefix(不同步磁盘)。

      • 示例:

      *.err               /dev/tty10
      auth.notice         root
      *.debug;mail.none;news.none   -/var/log/messages
      • 修改 syslog.conf 后需 killall -HUP syslogd 重读。

      When:所有 daemon 都应使用 syslog 而非自己管日志;用户程序也可用 syslog。

      Example

      // 摘自《The Linux Programming Interface》第 37 章
      openlog("mydaemon", LOG_PID | LOG_CONS, LOG_DAEMON);
      syslog(LOG_INFO, "Daemon started (PID=%ld)", (long) getpid());
      syslog(LOG_ERR, "Failed to open %s: %m", filename);  /* %m 替换 errno */
      setlogmask(LOG_UPTO(LOG_WARNING));   /* 只记录 WARNING 及以上 */
      closelog();

      三、关键图表

      daemon 化 7 步流程
      步骤 目的 实现

      1. fork + 父 exit

      父终止后 shell 显示提示;子非进程组 leader

      fork() → 父 _exit

      2. setsid

      新会话 leader;脱离控制终端

      setsid()

      3. 二次 fork + 父 exit

      子非 session leader;System V 防再获控制终端

      fork() → 父 _exit

      4. umask(0)

      不受父 shell mask 影响

      umask(0)

      5. chdir("/")

      cwd 在 root 可卸载

      chdir("/")

      6. close 所有 fd

      释放继承 fd;0/1/2 关闭

      循环 close 0..sysconf(_SC_OPEN_MAX)

      7. reopen 0/1/2 到 /dev/null

      stdio 调用不失败;防止后续 open 误用

      open("/dev/null") + dup2

      BSD 风格可省略步骤 3(BSD 需 TIOCSCTTY 才获控制终端)。

      syslog facility 表(表 37-1 节选)
      Facility 描述 SUSv3

      LOG_AUTH

      安全/认证(su 等)

      LOG_AUTHPRIV

      私有安全/认证(密码等敏感)

      LOG_CRON

      cron/at daemon

      LOG_DAEMON

      其他系统 daemon

      LOG_FTP

      ftp daemon

      LOG_KERN

      内核(用户程序不可用)

      LOG_LOCAL0-LOCAL7

      本地保留

      LOG_LPR

      行打印系统

      LOG_MAIL

      邮件系统

      LOG_NEWS

      Usenet

      LOG_SYSLOG

      syslogd 内部消息

      LOG_USER

      用户进程(默认)

      LOG_UUCP

      UUCP 系统

      syslog level 表(表 37-2)
      Level 描述

      LOG_EMERG

      紧急/panic(系统不可用)

      LOG_ALERT

      需立即处理(如系统数据库损坏)

      LOG_CRIT

      临界条件(如磁盘错误)

      LOG_ERR

      一般错误

      LOG_WARNING

      警告

      LOG_NOTICE

      正常但需关注

      LOG_INFO

      信息

      LOG_DEBUG

      调试

      四、思维导图

      mindmap
        root((第 37 章 守护进程))
          daemon 概述
            长寿 无终端 后台
            cron sshd httpd inetd
            命名以 d 结尾
            内核线程 ps 显示 中
            编写准则 第 38 章
          7 步 daemon 化
            1 fork 父 exit
            2 setsid 新会话
            3 二次 fork 防控制终端
            4 umask 0
            5 chdir /
            6 close 所有 fd
            7 reopen 0 1 2 dev null
            BSD 风格省二次 fork
          becomeDaemon
            flags 位掩码
            BD NO CHDIR
            BD NO CLOSE FILES
            BD NO REOPEN STD FDS
            BD NO UMASK0
            BD MAX CLOSE 8192
            glibc daemon 无 flags
          编写准则
            防内存 fd 泄漏
            单实例 PID 文件
            SIGTERM 优雅关闭
            init 5 秒后 SIGKILL
            handler 快速清理
          SIGHUP 重读配置
            daemon 无控制终端
            kernel 不发 SIGHUP
            handler 设 hupReceived
            主循环检查
            logrotate mv SIGHUP
            close open log 重读 config
          syslog API
            openlog ident option facility
            syslog priority format
            closelog
            setlogmask LOG UPTO
            LOG CONS NDELAY PID PERROR
            facility LOG USER DAEMON
            level LOG EMERG DEBUG
            m 替换 errno
            防 format string 攻击
          syslog 架构
            syslogd daemon
            dev log unix domain
            UDP 514 远程
            klogd 内核消息
            proc kmsg syslog 2
            syslog conf 分发
            terminal file FIFO user
            remote syslogd

      五、重点与易错点

      1. daemon 化 7 步缺一不可:每步有特定目的——脱离 shell/终端 + 防再获控制终端 + 文件系统清理 + fd 隔离。

      2. 二次 fork 的目的:System V 约定——非 session leader 永远不能获控制终端;BSD 风格可省略(BSD 需 TIOCSCTTY 才获)。

      3. glibc daemon() 函数无 flags:缺灵活性;TLPI becomeDaemon 用 flags 位掩码可选跳过某些步骤。

      4. close 所有 fd 不能用 closefrom——Linux 不可用;循环 close 0..sysconf(_SC_OPEN_MAX) 即可(带 BD_MAX_CLOSE=8192 fallback)。

      5. reopen stdin/stdout/stderr 到 /dev/null:防止库函数 stdio 失败 + 防止后续 open 误用 fd 1/2;/dev/null 写丢弃、读 EOF。

      6. daemon 长寿 → 必须防内存/fd 泄漏——只有 kill + restart 才能恢复;测试要覆盖长时间运行。

      7. daemon 单实例:cron/sshd 一个实例足够——用 PID 文件 + 文件锁实现(§55.6)。

      8. SIGTERM 优雅关闭:init 关机发 SIGTERM → 5 秒后 SIGKILL;handler 快速清理(flush log / close fd / unlink PID 文件)。

      9. SIGHUP 重读配置模式:daemon 无控制终端 → kernel 不发 SIGHUP;handler 设 hupReceived 标志 + 主循环检查;logrotate(8) 触发。

      10. hupReceived 必须 volatile sig_atomic_t——async-signal-safe;handler 只设标志避免复杂操作。

      11. syslog 三函数:openlog (建立连接 + 默认) + syslog (priority + format) + closelog (关闭);setlogmask 过滤 level。

      12. syslog priority = facility | level:facility 缺省用 openlog 设置或 LOG_USER;level 必须指定。

      13. %m 替换 errno——syslog 特有 printf 不支持;用于 errno 错误消息格式化。

      14. format-string 攻击防御:用户输入必须用 "%s" 包裹——syslog(priority, "%s", user_str) 而非 syslog(priority, user_str)

      15. syslog(2) vs syslog(3) 不同——前者内核消息(glibc klogctl 包装);后者用户 API。

      16. syslogd 收消息来源:/dev/log(Unix domain)+ UDP 514(远程)+ klogd(内核消息)。

      17. syslog.conf 规则facility.level action* 通配;; 多 selector;level=none 排除;@remote 远程;改后需 killall -HUP syslogd

      18. SETLOGMASK 默认允许所有 level——只丢弃不在 mask 中的 level;LOG_UPTO(level) 包括 level 及以上。

        • 跨章衔接:第 24-27 章 fork/exec 基础 → 第 34 章 setsid/控制终端 → 第 37 章 daemon 化;第 38 章 set-UID 程序编写准则(daemon 常以 root 运行);第 55 章 fcntl 锁 → 单实例 daemon(§55.6);第 60 章 inetd → 超级服务器 daemon;第 22 章信号 → SIGHUP/SIGTERM 处理。