2

我需要在临时容器/VM 中的(远程)shell 上为测试执行引擎运行基本上任意命令。有时这些泄漏后台进程会导致整个命令挂起。这可以归结为这个简单的命令:

$ sh -c 'sleep 30 & echo payload'
payload
$

在这里,backgroundedsleep 30扮演了泄露进程的角色(实际上将类似于dbus-daemon),而 echo 是我想要运行的实际内容。此处应将其sleep 30 & echo payload视为原子不透明示例命令。

上面的命令很好,并立即返回,因为 shell 和 sleep 的 stdout/stderr 都是 PTY。但是,当将命令的输出捕获到管道/文件时(毕竟,测试运行程序希望将所有内容保存到日志中),整个命令会挂起:

$ sh -c 'sleep 30 & echo payload' | cat
payload
# ... does not return to the shell (until the sleep finishes)

现在,这可以通过一些相当可笑的复杂 shell 魔法来解决,它确定 stdout/err 的 FD /proc/$$/fd/{1,2},迭代ls /proc/[0-9]*/fd/*并杀死每个也具有相同 stdout/stderr 的进程。但这涉及到大量脆弱的 shell 代码和昂贵的 shell 字符串比较。

有没有办法以更优雅、更简单的方式清理这些泄露的后台进程?setsid没有帮助:

$ sh -c 'setsid -w sh -c "sleep 30 & echo payload"' | cat
payload
# hangs...

请注意,进程组/会话并彻底杀死它们是不够的,因为泄漏的进程(如 dbus-daemon)通常会自行设置。

PS 我只能在这些环境中假设 POSIX shell 或 bash;没有 Python、Perl 等。

先感谢您!

4

3 回答 3

0

使用script -qfc而不是sh -c.

于 2015-09-02T10:33:05.567 回答
0

嗯,重新阅读后,我无法为您提供您想要的解决方案(使用 systemd 杀死它们)。我们想出的是简单地忽略这些进程,但当我们等待的单个进程完成时可靠地不会挂起。请注意,这与管道关闭明显不同。

另一个不完美但有用的选项是使用 prctl(2) 和 PR_SET_CHILD_SUBREAPER 成为本地收割者。这将允许您成为所有进程的父进程,否则这些进程将重新成为 init 的父进程。通过这种安排,您可以尝试杀死所有以您为 ppid 的进程。这很糟糕,但它是最接近使用 cgroups 的最佳方式。

但请注意,除非您以 root 身份运行此帮助程序,否则您会发现实际测试可能会产生一些潜伏且不会被杀死的 setuid 东西。这真是一个烦人的问题。

于 2015-09-02T10:09:48.277 回答
0

我们在 Launchpad 中进行并行测试时遇到了这个问题。我们当时最简单的解决方案 - 效果很好 - 只是确保没有进程共享 stdout/stdin/stderr (除了那些你实际上想要挂起的进程,如果他们还没有完成 - 例如测试工作人员本身)。

于 2015-09-02T09:58:08.843 回答