如果终端永远无法关闭,守护进程将其自身与控制终端分离有什么意义?

如果终端永远无法关闭,守护进程将其自身与控制终端分离有什么意义?

我读过的有关编写守护进程的所有资料都说守护进程应该将自己与控制终端分离,这样如果终端关闭,守护进程就不会收到 SIGHUP 信号。

但守护进程总是由 init 进程启动。htop显示我的 init 进程(systemd)没有控制终端,因此它生成的守护进程也没有控制终端。我不知道它在 SysVinit 中是如何工作的,但更改日志中有提示表明它要么没有控制终端,要么使用“/dev/console”。我可能是错的,但似乎这是一个特殊的内核 tty,始终存在且无法关闭。

我错过了什么吗?

我猜测这更多的是为每个守护进程启动一个新的进程会话,而不是从控制终端分离。当守护进程生成其子进程时,会话可以提供帮助。会话跟踪这些子进程。这使我们能够将他们全部杀死。

答案1

正如 @AndyDalton 和 @muru 指出的(谢谢!),SysVinit 和 Systemd 的工作方式不同。

在 systemd 中,守护进程仅由 init 进程生成。用于控制守护进程(如 systemctl)的 cli-tools 通过 IPC 向 init 进程发送命令,然后 init 进程完成这项工作。他们使用 sd-bus,他们自己的 D-bus 实现。如果 D-bus 系统消息总线无法正常工作,可能可以回退到使用套接字,但这需要 root 权限。 (我不确定它是否真的可以回落。这是我根据描述的猜测这个案例这次讨论

就 SysVinit 而言,向守护进程发送信号并生成守护进程是守护进程特定的 bash 脚本的工作。 init 进程在启动时运行此脚本。但如果您想通过 cli 手动启动守护进程,您可以自己运行此脚本。然后守护进程就成为 bash 进程的子进程并拥有一个控制终端。有关更多详细信息,请参阅互联网上的 sysvinit 守护程序脚本示例和“service”cli 工具。 “service”只是一个 bash 脚本。它可以在 debian 包“sysvinit-utils”中找到。看来 init 进程仅使用 PID 文件来跟踪正在运行的守护进程。

这就是 SysVinit 守护进程需要将自己与控制终端分离的原因。而新型守护进程(systemd、upstart 等)则不需要它。这也解释了这部分来自手动的:

  1. 由于新型守护进程在没有控制 TTY(但作为它们自己的会话领导者)的情况下被调用,因此应注意始终O_NOCTTYopen()可能引用 TTY 设备节点的调用上进行指定,以便不会意外获取控制 TTY。

结论:

  1. Systemd 支持 SysVinit 风格的双分叉守护进程(type=forking),但这不是推荐的编写守护进程的方式。不需要双分叉。

  2. 看来 systemd 为你做了setsid():

   Note that new-style init systems guarantee execution of daemon
   processes in a clean process context: it is guaranteed that the
   environment block is sanitized, that the signal handlers and mask
   is reset and that no left-over file descriptors are passed.
   Daemons will be executed in their own session, with standard
   input connected to /dev/null and standard output/error connected
   to the systemd-journald.service(8) logging service, unless
   otherwise configured. The umask is reset

这是守护进程不需要将自己与控制终端分离的另一个原因。他们已经在新的会话中。

  1. 守护进程是其会话的领导者。因此守护进程在打开控制终端时应小心,以免获取控制终端。但在现代操作系统上意外执行此操作有点困难如果 SIGHOP 只是重新加载配置的请求,那么获取控制终端可能不是那么大的问题。

  2. 我不确定,但似乎systemd 使用控制组来跟踪和终止守护进程的子进程那为什么需要setsid呢?我的情况是,现在是会议,或者对照组需要单独的会议——我不知道。可能会话从未用于此目的。但现在它使用对照组。

更新:

  1. @Uncle Billy 提到了可以通过终端发送到进程的其他信号。我做了一些研究。SIGINT、SIGQUIT 和 SIGSTP 由 tty 线路规则发送。如果设置了 termios 中 c_lflag 中的标志 ISIG,则发送这些信号。线路规则将信号发送到前台进程组。不至于壳。 Shell提前将前台进程组的pid报告给线路纪律。我找不到 ISIG 的默认值。但如果没有手动设置前台进程组(即我们的进程不是作业感知 shell),进程很可能不会收到这些信号。可能这也与终端是否控制无关。这更多的是关于工作控制。但我可能是错的。我想知道不知道工作的 shell(如 Bourne shell)如何处理 SIGINT。命令“trap”表明他们对此有不同的机制。

相关内容