2012-02-03 15 views
6

Estoy buscando un método general para iniciar y luego matar a un proceso R, incluyendo posiblemente todas las bifurcaciones u otros procesos que invoca.Recursively matar proceso R con niños en linux

Por ejemplo, un usuario ejecuta un script como el siguiente:

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") 

Después de que el usuario salga de la sesión I, los procesos hijos están aún en marcha:

[email protected]:~$ 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 
[email protected]:~$ 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 

Para empeorar las cosas, su su id. de proceso principal es 1, lo que dificulta su identificación. ¿Hay algún método para ejecutar un script R de una manera que me permita matar recursivamente el proceso y sus hijos en cualquier momento?

Editar: así que no quiero tener que entrar manualmente para buscar & matar procesos. Además, no quiero matar a todos los procesos R, ya que puede haber otros que lo estén haciendo bien. Necesito un método para matar un proceso específico y todos sus hijos.

+0

Simplemente no mate PID 1 - * no * hará lo que quiera. Bueno, en realidad lo hará ... algo así como ... – thkala

+0

lol @thkala oh sí, hará más de lo que esperaba :) y ¿por qué no simplemente eliminar este binario? –

Respuesta

8

Esto es principalmente acerca de la parte multinúcleo. Los niños están esperando que recopile los resultados: consulte ?collect. Normalmente, nunca debe usar parallel sin una disposición para limpiar, generalmente en on.exit. multinúcleo se limpia en funciones de alto nivel como mclapply, pero si usa funciones de nivel inferior, es su responsabilidad realizar la limpieza (ya que multinúcleo no puede saber si dejó a los niños ejecutando intencionalmente o no).

Su ejemplo es realmente falso, porque ni siquiera considera recolectar resultados. Pero de todos modos, si eso es realmente lo que quieres, tendrás que hacer la limpieza en algún momento. Por ejemplo, si desea terminar todos los niños a la salida, se podría definir .Last así:

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

Una vez más, lo anterior es no un método recomendado para hacer frente a esto - es más bien un último recurso. Usted realmente debe asignar trabajos y recoger resultados como

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

En cuanto a la cuestión general proceso hijo - init hereda los niños sólo después de R se cierra, pero en .Last aún se pueden encontrar sus PIDs ya que existe el proceso padre en ese momento para que pueda realizar una limpieza similar a la del caso multinúcleo.

+0

Gracias. El problema es que los usuarios algunas veces (supongo que involuntariamente) dejan un lío en mis servidores. Intento ponerlos en una caja de arena restringiendo los permisos y limpiarlos cuando sea posible. – Jeroen

+0

+1 Este es un buen consejo y ayuda a extender la documentación. No es que la documentación para 'multicore' sea mala, querido autor de' multicore', pero dominar 'fork' y' collect' es más fácil con más ejemplos y consejos. – Iterator

+0

@Jeroen Eso es justo. Lamentablemente, limpiar en R es voluntario. Sin embargo, puede escribir una pequeña función C y registrarla con 'atexit' para forzar la limpieza en todos los casos (excepto en caso de fallas, solo los manipuladores de señales lo ayudarán). –

4

Antes de el usuario abandona la sesión R, los procesos que desea eliminar tendrán la ID del proceso principal igual al ID del proceso de la sesión que los inició. Quizás pueda utilizar los ganchos .Last o .Last.sys (consulte help(q)) para eliminar todos los procesos con el PPID apropiado en ese punto; esos pueden ser suprimidos con q(runLast=FALSE), por lo que no es perfecto, pero creo que es la mejor opción que tiene.

Después de que el usuario cierra la sesión R, no hay manera confiable de hacer lo que quiera - el único registro mantiene el núcleo de la paternidad es el proceso PPID que se ve en ps -ef, y cuando un proceso padre salidas, esa información se destruye, como has descubierto.

Tenga en cuenta que si uno de los niños procesos tenedores, el nieto tendrá PPID igual al niño 's PID, y que conseguirá restablecer a 1 cuando los niño salidas, lo que podría hacer antes el abuelo sale. Por lo tanto, no existe una manera confiable de detectar todos los de los descendientes de un proceso en general, incluso si lo hace antes de que el proceso finalice. (Se escucha que "cgroups" proporciona una forma, pero uno no está familiarizado con los detalles; en cualquier caso, esa es una característica opcional que solo proporcionan algunas iteraciones/configuraciones del kernel de Linux, y no está disponible en ninguna otra parte).

+1

Es posible que también desee ver 'ps aux --forest', que proporciona la misma información que el PPID (debe hacerse antes de que el padre fallezca), de forma más gráfica. Es útil si tiene varias generaciones de procesos. –

+1

Desafortunadamente, incluso antes de que el proceso principal fallezca, los hijos que se hayan iniciado con el comando system() tendrán el ID padre 1. – Jeroen

+0

@Jeroen No necesariamente: por ejemplo 'system (" sleep 300 ")' no, pero ' sistema ("dormir 300 y") 'lo hará. Sin embargo, dependiendo de qué tan complicado fue el comando y precisamente qué sistema operativo, biblioteca C y '/ bin/sh' tiene, puede haber un proceso intermedio 'sh' dando vueltas y confundiendo el problema. – zwol

1

Creo que la última parte de la pregunta es más una consideración del shell, en lugar del kernel. (Simon Urbanek ha respondido la parte multicore mejor que casi cualquier otra persona, ya que él es el autor :) :)

Si está utilizando bash, puede encontrar el PID del proceso hijo iniciado más recientemente en $!. Puede agregar los PID y luego asegúrese de eliminarlos cuando cierre R.

Si quiere ser realmente gonzo, podría almacenar el PID padre (es decir, la salida de Sys.getpid()) y el PID infantil en un archivo y tener un daemon de limpieza que verifica si existe o no el PID padre y, de no ser así, mata a los huérfanos. Sin embargo, no creo que sea tan fácil obtener un paquete llamado oRphanKilleR en CRAN.

Aquí es un ejemplo de anexar el niño PID a un archivo:

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

Usted puede modificar esto para crear su propio comando de shell y utilizar comandos de R tempfile() para crear un archivo temporal (no obstante, que desaparecerá cuando finaliza la instancia R, a menos que haga un esfuerzo especial para preservar el archivo a través de permisos).

Para otras ideas ingeniosas, vea this other post on SO.

También puede crear un bucle do while en el shell que verificará si existe o no un PID en particular. Mientras está, el ciclo duerme. Una vez que termina el ciclo (porque el PID ya no se usa), el script matará a otro PID.

Básicamente, creo que la solución estará en shell scripting, en lugar de R.

Cuestiones relacionadas