linux中带子进程的递归kill R进程

linux中带子进程的递归kill R进程,linux,r,shell,process,fork,Linux,R,Shell,Process,Fork,我正在寻找一种通用方法来启动并终止一个R进程,可能包括它调用的所有fork或其他进程 例如,用户运行如下脚本: 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") .Last <- function(...) { col

我正在寻找一种通用方法来启动并终止一个R进程,可能包括它调用的所有fork或其他进程

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

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")
 .Last <- function(...) {
     collect(wait=FALSE)
     all <- children()
     if (length(all)) {
         kill(all, SIGTERM)
         collect(all)
     }
 }
更糟糕的是,它们的父进程id为1,因此很难识别它们。是否有一种方法可以运行R脚本,使我能够随时递归地终止进程及其子进程

编辑:所以我不想手动进入搜索和终止过程。另外,我不想杀死所有的R进程,因为可能还有其他进程做得很好。我需要一个方法来杀死一个特定进程及其所有子进程。

在用户退出R会话之前,要杀死的进程的父进程ID将等于启动它们的会话的进程ID。您可能可以使用
.Last
.Last.sys
钩子(请参阅
帮助(q)
)在该点上用适当的PPID终止所有进程;这些都可以通过
q(runLast=FALSE)
来抑制,因此它并不完美,但我认为这是您最好的选择

用户退出R会话后,没有可靠的方法来做您想要做的事情——内核保存的进程父级关系的唯一记录是您在
ps-ef
中看到的PPID,并且当父进程退出时,该信息会被销毁,正如您所发现的那样


请注意,如果其中一个子进程分叉,孙子进程的PPID将等于子进程的PID,当子进程退出时,PPID将重置为1,这可能会在祖辈进程退出之前执行。因此,通常没有可靠的方法来捕获进程的所有子代,即使在进程退出之前这样做。(人们听说“cgroups”提供了一种方法,但对细节并不熟悉;在任何情况下,这是一种可选功能,只有Linux内核的某些迭代/配置提供,在其他地方根本不可用。)

我相信问题的后半部分更多的是考虑shell,而不是内核。(西蒙·乌尔巴内克(Simon Urbanek)对
多核部分的回答比其他人都好,因为他是作者。:)

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

如果您想成为真正的gonzo,您可以将父PID(即,
Sys.getpid()
的输出)和子PID存储在一个文件中,并使用一个清理守护进程来检查父PID是否存在,如果不存在,则杀死孤立项。不过,我认为在CRAN上安装一个名为“孤儿杀手”的软件包并不是那么容易

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

system('(sleep 20) & echo $! >> ~/childPIDs.txt', wait = FALSE)
您可以修改此命令来创建自己的shell命令,并使用R的
tempfile()
命令来创建临时文件(但当R实例终止时,临时文件将消失,除非您通过权限采取特殊措施来保留该文件)

有关其他一些聪明的想法,请参阅

您还可以在shell中创建一个
do-while
循环,该循环将检查特定PID是否存在。此时,循环处于休眠状态。一旦循环终止(因为PID不再使用),脚本将终止另一个PID

基本上,我认为您的解决方案将采用shell脚本,而不是R。这主要是关于多核部分。孩子们正在等待您收集结果-请参阅
?收集
。通常,在没有清理规定的情况下,您不应使用
并行
,通常是在
on.exit
中。多核在高级函数(如
mclappy
)中进行清理,但如果您使用较低级别的函数,则您有责任执行清理(因为多核无法知道您是否有意让子级运行)

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

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")
 .Last <- function(...) {
     collect(wait=FALSE)
     all <- children()
     if (length(all)) {
         kill(all, SIGTERM)
         collect(all)
     }
 }

.Last不要杀死PID 1-它不会做你想做的事。嗯,实际上它会。。。有点……lol@thkala哦,是的,它会比他所期望的做得更多:)为什么不简单地删除这个二进制文件呢?您可能还想看看
ps aux--forest
,它提供了与PPID相同的信息(必须在父级死亡之前完成),更形象。如果您有几代进程,这很有用。不幸的是,即使在父进程死亡之前,使用system()命令启动的子进程也会有父id 1。@Jeroen不一定:例如
system(“sleep 300”)
不会,但
system(“sleep 300&”)会。但是,根据命令的复杂程度以及您所使用的操作系统、C库和
/bin/sh
,可能会有一个中间
sh
过程悬而未决并混淆了问题。谢谢。问题是,用户有时(我想是无意中)会在我的服务器上留下混乱。我试图通过限制权限和尽可能地清理权限来对其进行沙箱处理。+1这是一个很好的建议,有助于扩展文档。亲爱的
multicore
的作者,并不是因为
multicore
的文档不好,而是因为有了更多的例子和建议,掌握
fork
collect
就更容易了。@Jeroen这很公平。不幸的是,R区的清理是自愿的。但是,您可以编写一个小C函数并将其注册到
atexit
,以便在所有情况下强制清除(崩溃除外-只有信号处理程序会有帮助)。@Iterator哦,您不应该使用
fork
-甚至还有一个警告ab