man 7 daemon
当传统的 SysV 守护进程启动时,它应该执行以下步骤作为初始化的一部分。请注意,这些步骤对于新型守护程序来说是不必要的(见下文),并且只有在必须与 SysV 兼容时才应实施。
[...]
6. 在子进程中,调用setsid()从任何终端分离并创建一个独立的会话。
7. 在子进程中,再次调用 fork(),确保守护进程永远不会再次重新获取终端。
但将此与没有 SysV 兼容性痕迹的启动进程进行比较:
$ ps -efj
UID PID PPID PGID SID C STIME TTY TIME CMD
root 1 0 1 1 0 May10 ? 00:06:44 /sbin/init
...
root 185 1 185 185 0 May10 ? 00:09:48 /lib/systemd/systemd-journald
root 16434 1 16434 16434 0 May26 ? 00:00:11 /usr/sbin/rsyslogd -n
rsyslog.service
和的进程systemd-journal.service
都是会话领导者 (SID = PID)。
看起来,如果这些程序被配置为登录到 TTY,它们就会将 TTY 作为控制终端,并在 TTY 挂起/接收到 Ctrl+C 时分别收到不需要的/致命信号。除非他们记得在打开 TTY 文件时设置 O_NOCTTY。
如果您的程序支持将消息写入自定义文件,那么在编写或转换程序以作为没有任何 SysV 兼容性的 systemd 服务运行时,这似乎是一个小陷阱。这个提倡systemd风格的文档似乎没有指出这一点。该文档暗示了相反的情况,坚持认为必须使用双分叉来避免 SysV 上的这种情况,然后在描述本机 systemd 服务将使用的步骤时没有提到这一点。
那是对的吗? systemd 是否提供了一些针对我忽略的保护措施,或者该问题是否在 systemd 文档的其他地方指出?
答案1
systemd 是否针对这种情况提供了一些保护 [...]?
你假设它应该。相反,请考虑诸如 之类的设置TTYPath
和诸如 之类的服务[email protected]
。获得控制终端的能力实际上是必要的,以便服务管理可以包含 TTY 登录服务,而这正是需要做到的。
真正防止这种情况发生的是放弃在 处自动分配控制终端open()
,并放弃旧的语义。或者会防止它。在 Linux 上情况并非如此,但在 FreeBSD、NetBSD、OpenBSD 和 Hurd 上,现在O_NOCTTY
的 标志open()
完全是多余的。这仅有的获取控制终端的方法是通过明确要求它,使用ioctl(…TIOSCTTY)
.事实上,自 4.4BSD 时代以来,这种情况已经持续了近四分之一个世纪。
与此同时,在 Linux 上养成的习惯也已经存在很长时间了,早在 systemd 之前:O_NOCTTY
到处。 ☺
(是的,GNU 和 musl C 库不会为您提供此功能fopen()
。这是仍然是一种有用机制的几个原因之一fdopen()
。)
使用 nosh 工具集进行服务管理service-manager
对此采取了略有不同的策略。不是总是将守护进程设置为会话领导者,而是为每个服务分配自己的内核会话对象,然后该对象就看不到任何用处,只有特定的服务也会setsid
显式链接;例如使用的服务,当然是设置控制终端的服务,以及服务。 (如服务源中所述,调用自身。)ttylogin@*
open-controlling-tty
agetty@*
agetty
getty@*
mgetty
setsid()
进一步阅读
- 乔纳森·德博因·波拉德 (2018)。
setsid
。小吃指南。软件。 - 乔纳森·德博因·波拉德 (2018)。
open-controlling-tty
。小吃指南。软件。 - 乔纳森·德博因·波拉德 (2015)。 ”继承和守护进程谬误
”。
service
命令不再有问题。诺什简介。软件。 - 乔纳森·德博因·波拉德 (2001)。 ”不要
fork()
为了“将守护进程置于后台”。”。设计 Unix 守护程序时要避免的错误。经常给出的答案。 - 乔纳森·德博因·波拉德。 ”虚拟终端登录”。小吃指南。软件。
- 乔纳森·德博因·波拉德。 ”真实终端登录”。小吃指南。软件。
- http://git.musl-libc.org/cgit/musl/tree/src/stdio/__fmodeflags.c
- https://github.com/lattera/glibc/blob/a2f34833b1042d5d8eeb263b4cf4caaea138c4ad/libio/fileops.c#L274
答案2
systemd 不保护服务程序不获取控制终端。当打开用户指定的日志文件时,他们必须使用该O_NOCTTY
标志来保护自己。
$ rpm -q systemd
systemd-238-8.git0e0aa59.fc28.x86_64
$ systemctl cat test
# /etc/systemd/system/test.service
[Service]
Type=simple
ExecStart=/bin/sh -c "exec cat </dev/tty10 >/dev/tty10"
$ systemctl status test
● test.service
Loaded: loaded (/etc/systemd/system/test.service; static; vendor preset: disabled)
Active: active (running) since Fri 2018-06-01 11:28:41 BST; 1min 35s ago
Main PID: 12173 (cat)
Tasks: 1 (limit: 4915)
Memory: 180.0K
CGroup: /system.slice/test.service
└─12173 cat
Jun 01 11:28:41 alan-laptop systemd[1]: Started test.service.
$ ps -ejf
UID PID PPID PGID SID C STIME TTY TIME CMD
...
root 12173 1 12173 12173 0 11:28 tty10 00:00:00 cat
我还确认切换到 tty10 并按 Ctrl+C 会停止该cat
过程。