我倾向于一个程序“master”,它管理一组同时运行的子进程“slave”。根据需要启动和终止子流程。许多这些子流程使用启动脚本。
输出pstree
看起来像这样(摘录,master 是用 Java 实现的,两个 slave 是通过脚本启动的):
systemd───java─┬─sh───slave
├─slave
└─sh───slave
以前,启动脚本将奴隶的输出重定向到日志文件。决定主机也应该处理从机的输出。通过添加这样的缓冲读取器来扩展 master 的实现:
process = Runtime.getRuntime().exec(cmd);
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
while (null != (line = br.readLine())) {
// handle slave output here
}
SIGTERM
然后,系统对已被主人杀死(发送)但实际上仍在运行的奴隶产生了严重的问题。我注意到这只发生在满足两个标准的奴隶身上:
- 他们使用了启动脚本
- 他们很少写到标准输出
由于 master 没有杀死 slave,而只是杀死了它的直接父级(shell 解释器),所以 slave 现在归 init 所有。就我而言, systemd 似乎是默认的reaper。pstree
看起来像这样:
systemd─┬─java───sh───slave
└─slave
在功能上,我通过明确杀死奴隶的整个家庭来解决这个问题。然而我仍然想知道:
为什么 systemd 只有在写入标准输出(或错误)并且只有在标准输出之前被另一个进程读取时才会杀死孤儿?
这个问题相当冗长。根据要求,我可以提供一个最小的代码示例来重现所描述的行为。
这可能不是 systemd 这样做的。
相反,当进程尝试写入读取端已关闭的管道时,进程会被 SIGPIPE 杀死——这符合“标准输出之前已被另一个进程读取”的描述。