我有一个命令行实用程序,它生成一个守护进程,然后该进程变得孤立。如何使守护进程与终端模拟器一起退出?
答案1
一旦一个进程成为守护进程并忽略 SIGHUP(或更可能的是,它已成为自己的会话领导者并位于自己的进程组中,并且它永远不会发送SIGHUP),当终端仿真器关闭时终止它的唯一方法是记录其 PID 并发送致命信号:
# pid=$(</var/run/some-daemon.pid)
# trap "kill $pid" EXIT
这是假设您使用的是 bash 或兼容的 shell。获取守护进程 PID 的方式可能有所不同,但大多数守护进程都会将其 PID 写入文件。真正的魔力是trap
命令。
正如 thrig 所指出的,该技术假设守护进程写入一个 PID 文件并且您正常退出 shell(使用exit
、^D 等)。这种技术可能很脆弱,但很简单。
答案2
进程守护进程的目的是不是依赖于它的父级。
要依赖任意 pid,您可以为其打开 pidfd,请参阅pidfd_open
,它有示例代码,但您可能更epoll
喜欢poll
。
因此,在守护进程本身之前,您的实用程序可以尝试寻找它想要关心的某些进程,也许检查它是否明显由 shell 启动并依赖于 shell 的父级,否则可能采用 pid cmdline 选项。对我来说,祖父母将是我的 tmux 服务器,而不是终端模拟器;目前尚不清楚什么是正确的,猜测它取决于上下文,也许纯粹是偏好。
答案3
守护进程可能执行双分叉,或者可能不支持 PID 文件,或者可能在进程列表中随机更改其名称。如果守护进程不做任何先前的情况,它都可以由 shell$!
变量跟踪,或者它可能有一个文件,您可以cat
祈祷内容正确(如果守护进程崩溃并且同时有其他东西获取该 PID,则 PID 将不正确),或者也许pkill(1)
可以在 shellEXIT
陷阱中找到并杀死该进程。 (如果您用其他程序替换 shell,shellEXIT
陷阱可能不会触发exec some-other-program
,因此请务必记住不要这样做。)这听起来不太可靠吗?那是因为它不太可靠。
守护进程可能有一个在前台运行的选项,在这种情况下,您可以按照 的方式启动它your-daemon --with-the-foreground-flag &
,可能会根据需要重定向它的任何输出,在这种情况下,SIGHUP
当父 shell 进程消失时, 应该适用于它。 (但如果您是exec
其他程序的 shell,则可能不会。)
如果您使用的是 Linux,那么人们可能会将终端和守护程序以及其他所有内容放入自定义 cgroup 中,并在会话结束时杀死整个组。systemd 因这样做而遇到了麻烦tmux
默认情况下。这应该比双分叉逃逸$!
或没有 PID 文件,或者可能是不正确的 PID 文件,或者猜测进程列表中的进程名称更可靠。此外,cgroup 不会关心 shell 何时exec
成为某个不同的进程。