在linux中用子节点递归杀死R进程

时间:2012-02-03 00:49:32

标签: linux r shell process fork

我正在寻找一种通用的方法来启动然后杀死一个R进程,包括它可能调用的所有forks或其他进程。

例如,用户运行如下脚本:

library(multicore);
for(i in 1:3) parallel(foo <- "bar");
for(i in 1:3) system("sleep 300", wait=FALSE);
for(i in 1:3) system("sleep 300&");
q("no")

用户退出R会话后,子进程仍在运行:

jeroen@jeroen-ubuntu:~$ ps -ef | grep R
jeroen    4469     1  0 16:38 pts/1    00:00:00 /usr/lib/R/bin/exec/R
jeroen    4470     1  0 16:38 pts/1    00:00:00 /usr/lib/R/bin/exec/R
jeroen    4471     1  0 16:38 pts/1    00:00:00 /usr/lib/R/bin/exec/R
jeroen    4502  4195  0 16:39 pts/1    00:00:00 grep --color=auto R
jeroen@jeroen-ubuntu:~$ ps -ef | grep "sleep"
jeroen    4473     1  0 16:38 pts/1    00:00:00 sleep 300
jeroen    4475     1  0 16:38 pts/1    00:00:00 sleep 300
jeroen    4477     1  0 16:38 pts/1    00:00:00 sleep 300
jeroen    4479     1  0 16:38 pts/1    00:00:00 sleep 300
jeroen    4481     1  0 16:38 pts/1    00:00:00 sleep 300
jeroen    4483     1  0 16:38 pts/1    00:00:00 sleep 300
jeroen    4504  4195  0 16:39 pts/1    00:00:00 grep --color=auto sleep

更糟糕的是,他们的父进程ID为1,因此难以识别它们。是否有一种方法以允许我随时以递归方式终止进程及其子进程的方式运行R脚本?

编辑:所以我不想手动进入搜索&amp;杀死进程。此外,我不想杀死所有R进程,因为可能有其他人正在做得很好。我需要一种方法来杀死一个特定的进程及其所有子进程。

3 个答案:

答案 0 :(得分:8)

这主要是关于多核部分。孩子们正在等你收集结果 - 见?collect。通常情况下,如果没有要清理的条款,您通常不应使用parallel,通常在on.exit多核在高级函数(如mclapply)中进行清理,但如果使用较低级别的函数,则执行清理是您的责任(因为 multicore 无法知道如果你让孩子有意或无意地跑步。)

你的例子真的是假的,因为你甚至不考虑收集结果。但无论如何,如果这真的是你想要的,你将不得不在某个时候进行清理。例如,如果要在退出时终止所有子项,可以像这样定义.Last

 .Last <- function(...) {
     collect(wait=FALSE)
     all <- children()
     if (length(all)) {
         kill(all, SIGTERM)
         collect(all)
     }
 }

同样,上面的是推荐的解决方法 - 这是最后的手段。您应该真正分配工作并收集结果,如

jobs <- lapply(1:3, function(i) parallel({Sys.sleep(i); i}))
collect(jobs)

至于一般过程子问题 - init仅在R退出后继承子项,但在.Last中,您仍然可以找到它们的pid,因为父进程在那时存在所以您可以执行类似于多核情况下的清理。

答案 1 :(得分:4)

用户退出R会话之前,您要终止的进程的父进程ID将等于启动它们的会话的进程ID。您也许可以使用.Last.Last.sys挂钩(请参阅help(q))在该点使用适当的PPID终止所有进程;那些可以用q(runLast=FALSE)来抑制,所以它并不完美,但我认为这是你最好的选择。

用户退出R会话后,没有可靠的方法来执行您想要的操作 - 内核保留进程父级的唯一记录是您在ps -ef中看到的PPID ,当父进程退出时,正如您所发现的那样,该信息被销毁。

请注意,如果其中一个处理分叉,则孙子将具有等于的PID的PPID,并且当< em> child 退出,在祖父母退出之前可能会退出。因此,即使您在进程退出之前这样做,也没有可靠的方法来捕获进程后代的所有。 (人们听说“cgroups”提供了一种方法,但是人们不熟悉细节;无论如何,这是一个可选功能,只有Linux内核的一些迭代/配置提供,并且在其他地方都不可用。)< / p>

答案 2 :(得分:1)

我认为问题的后半部分更多地是对shell的考虑,而不是内核。 (Simon Urbanek比其他任何人都更好地回答了multicore部分,因为他是作者。:))

如果您正在使用bash,则可以在$!中找到最近启动的子进程的PID。您可以聚合PID,然后确保在关闭R时将其关闭。

如果你想成为真正的奇闻趣事,你可以将父PID(即Sys.getpid()的输出)和子PID存储在一个文件中,并有一个清洁守护进程来检查父PID是否存在,如果不,杀死孤儿。但我认为将一个名为oRphanKilleR的软件包放到CRAN上并不容易。

以下是将子PID附加到文件的示例:

system('(sleep 20) & echo $! >> ~/childPIDs.txt', wait = FALSE)

您可以修改此命令以创建自己的shell命令并使用R的tempfile()命令创建临时文件(虽然在R实例终止时将消失,除非您特别努力保留文件通过权限)。

对于其他一些聪明的想法,请参阅this other post on SO

您还可以在shell中创建一个do while循环,以检查特定PID是否存在。虽然它是,但循环睡眠。一旦循环终止(因为PID不再使用),脚本将终止另一个PID。

基本上,我认为您的解决方案将采用shell脚本,而不是R。