如何使用Ruby检查特定pid的进程是否在运行?

52
如果有多种方法,请列出它们。我只知道一种方法,但我想知道是否有更干净、更符合Ruby的方法。
9个回答

62

Process.getpgidProcess::kill两种方法的区别在于,当pid存在但是由其他用户拥有时,发生的操作不同。 Process.getpgid会返回一个答案,Process::kill会抛出异常(Errno::EPERM)

基于这一点,我推荐使用Process.getpgid,即使只是为了避免捕获两个不同的异常。

下面是我使用的代码:

begin
  Process.getpgid( pid )
  true
rescue Errno::ESRCH
  false
end

如何终止另一个用户拥有的进程? - Luccas
3
(Process.getpgid(pid) rescue nil).present? => (Process.getpgid(pid) rescue nil).present? - Lev Lukomsky

54

如果这是您期望“拥有”的进程(例如,您正在使用它来验证您控制的进程的pid),则可以向其发送sig 0。

>> Process.kill 0, 370
=> 1
>> Process.kill 0, 2
Errno::ESRCH: No such process
    from (irb):5:in `kill'
    from (irb):5
>> 

有趣的方法!我会调查一下。 - Pistos
3
当,你比我先了。 我看到“新答案已发布”的提示,我的心情很沉重 :( - John T
但是信号0是什么?一个空操作吗? - Pistos
1
根据我的 man 页面:然而,值为 0 将导致执行错误检查(不发送任何信号)。这可以用来检查 pid 的有效性。 - Dustin
哎呀,我正在查看信号的手册页面等等。没想到要查看kill的手册页面。:) 谢谢,Dustin。 - Pistos
9
不幸的是,如果进程处于“僵尸”状态,这也会返回1,因此不能告诉您进程是否死亡,只能告诉您系统中是否存在具有该PID的进程(例如,如果您有一个打开的流到进程,即使进程已终止,这也将返回1)。还要注意,操作系统可以重新使用PID... - rogerdpack

47

@John T,@Dustin:实际上,各位,我查阅了Process rdocs文档,并且看起来:

Process.getpgid( pid )

是一种应用相同技术的较少暴力的方法。

1
我发现最新版Ubuntu上会出现误报(它报告正在运行,而实际上并没有)。我不知道为什么,但在这个帖子中只有@balu的解决方案对我有效。 - iain
我猜这不是跨平台的解决方案。(它在Windows上不起作用,对吧?) - Ajedi32
4
这里的暗示是 kill 0 是“暴力”的,这是一个错误。文档中写道:“如果信号是0,则不发送任何信号,但仍要进行错误检查;这可用于检查进程ID或进程组ID是否存在。” - Ray
1
在Windows机器上遇到“NotImplementedError(getpgid()函数在此机器上未实现)”,需要帮助吗? - Manivannan Jeganathan
@ManivannanJeganathan 这在Windows上不起作用。请使用另一种解决方案。(也许这个可以)。 - Ajedi32

32

对于子进程,发送信号等其他解决方案无法按预期工作:它们会指示该进程仍在运行,而实际上已经退出。

如果您想要检查自己生成的进程,可以使用Process.waitpid。如果使用Process::WNOHANG标志,则调用不会阻塞,只要子进程尚未退出,就会返回nil

示例:

pid = Process.spawn('sleep 5')
Process.waitpid(pid, Process::WNOHANG) # => nil
sleep 5
Process.waitpid(pid, Process::WNOHANG) # => pid
如果pid不属于子进程,将抛出异常(Errno::ECHILD: No child processes)。
对于Process.waitpid2同样适用。

2
我发现这比上述解决方案更可靠。谢谢! - iain
1
谢谢,这是关于生成进程最可靠的答案。对于非由我们生成的进程,其他解决方案也很好。 - akostadinov
这似乎是唯一在Windows上有效的答案,至少对于我来说:D - DarkWiiPlayer

7
这是我一直以来的做法:
def alive?(pid)
  !!Process.kill(0, pid) rescue false
end

通常情况下,您不希望像那样使用 rescue,但在这种情况下似乎是合理的。 - Adam Lassek

5
你可以尝试使用:
Process::kill 0, pid

其中pid是进程ID,如果该进程正在运行,则应返回1。


4
在 Linux 中,您可以使用 proc 文件系统获取正在运行的程序的许多属性:
File.read("/proc/#{pid}/cmdline")
File.read("/proc/#{pid}/comm")

0

我之前处理过这个问题,昨天将其编译成了 "process_exists" gem。

它向具有给定 pid 的进程发送空信号(0)来检查是否存在。即使当前用户没有权限向接收进程发送信号,它也能正常工作。

用法:

require 'process_exists'

pid = 12
pid_exists = Process.exists?(pid)

0
一个只适用于 *nix 系统的方法是通过调用 ps 命令,并检查返回字符串中是否存在 \n (换行符) 分隔符。
示例 IRB 输出如下:
1.9.3p448 :067 > `ps -p 56718`                                                          
"  PID TTY           TIME CMD\n56718 ttys007    0:03.38 zeus slave: default_bundle   \n"

封装为方法

def process?(pid)  
  !!`ps -p #{pid.to_i}`["\n"]
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接