我想在Ruby(MRI)进程的控制下运行一个外部进程。
有没有办法在Ruby下启动一个长时间/无限期运行的作业,然后通过编程控制其终止?我原以为这会相对容易,但似乎真的很难找到一种方法来做到这一点。
一些不起作用的示例代码:
pid = fork { system "yes > /dev/null" }
sleep 1 # yes is happily running
Process.kill "TERM", pid
如果我把它作为一个Ruby文件来运行,它运行得很愉快。然而,如果我选择pgrep yes
,我会看到yes
仍在运行。如果Ipkill yes
,它会成功消失。KILL
信号并不比TERM
信号更成功。
如何更改此代码以在后台运行yes
,然后允许其终止?
(如果它很重要,我对此表示怀疑,这是macOS上的MRI Ruby 2.3(哈哈)10.11。)
您有正确的想法,但您错过了一件微妙的事情。调用fork
会创建一个子进程,但system
也会这样做,因为它是作为fork
/exec
对实现的,所以实际上你杀死了错误的进程。
你可以判断你是否在那里puts pid
,看看你试图杀死哪个进程。您试图杀死的进程通常是实际运行的yes
进程的PID减去1。
你可能想要的:
pid = fork do
exec "/usr/bin/yes", out: File::NULL
end
sleep 1
Process.kill 'TERM', pid
使用exec
时必须非常小心,因为只要有一点点不规则的迹象,Ruby就会创建一个shell进程来执行您的命令,这会引入另一个子进程。杀死它实际上不会杀死yes
命令。在这里,我使用了out:
选项来抑制STDOUT
,而不是依靠> /dev/null
技巧来自动接合外壳包装器。
在一些快速测试中,我发现调用exec 'yes'
会导致启动一个中间shell,但指定完整路径可以避免这种情况。