2009-04-30 7 views
22

Tengo un proceso de ejecución larga y necesito que inicie otro proceso (que también se ejecutará durante un buen tiempo). Necesito solo comenzarlo, y luego olvidarme por completo.¿Cómo disparar y olvidar un subproceso?

Logré hacer lo que necesitaba sacando un código del libro Programación Ruby, pero me gustaría encontrar la mejor/correcta manera, y entender lo que está pasando. Aquí es lo que tengo inicialmente:

exec("whatever --take-very-long") if fork.nil? 
Process.detach($$) 


Por lo tanto, este es el camino, o de qué otra manera debería hacerlo?

Después de comprobar las respuestas a continuación que terminó con este código, que parece tener más sentido:

(pid = fork) ? Process.detach(pid) : exec("foo") 


lo agradecería una explicación sobre cómo fork obras. [tengo eso ya]

Se separó $$ derecho? No sé por qué funciona esto, y realmente me encantaría tener una mejor comprensión de la situación.

Respuesta

20

La función fork separa su proceso en dos.

Ambos procesos reciben el resultado de la función. El niño recibe un valor de cero/nil (y, por lo tanto, sabe que es el hijo) y el padre recibe el PID del niño.

Por lo tanto:

exec("something") if fork.nil? 

hará que el inicio proceso hijo "algo", y el proceso padre será continuar con el lugar donde estaba.

Tenga en cuenta que exec()reemplaza el proceso actual con "algo", por lo que el proceso secundario nunca ejecutará ningún código de Ruby posterior.

Parece que la llamada a Process.detach() podría ser incorrecta. Hubiera esperado que tuviera el PID del niño en él, pero si leo correctamente tu código, en realidad está desconectando el proceso principal.

+1

Ok, creo que tengo el .nil? comprobar. En el niño realmente no devuelve 0, devuelve nil. Entonces el ejecutor se ejecutará si estás en el niño. Entonces, ¿cómo capturaría la otra devolución de la horquilla, la que devuelve la identificación del proceso secundario? De esta forma podría usarlo directamente en lugar de depender de $$. – kch

+2

simplemente llame al tenedor y capture el resultado. luego, si tienes cero, haz el ejecutivo, de lo contrario, eres el padre. – Alnitak

+0

Esta es una definición tan brillante y concisa de 'horquilla'. Buen trabajo, gracias. – KomodoDave

28

Alnitak tiene razón. Aquí está una manera más explícita a escribirlo, sin $$

pid = Process.fork 
if pid.nil? then 
    # In child 
    exec "whatever --take-very-long" 
else 
    # In parent 
    Process.detach(pid) 
end 

La finalidad de detach es sólo para decir: "No me importa cuando el hijo termina" para evitar zombie processes.

+1

Hola, estaba jugando con mis conocimientos recién adquiridos, y básicamente llegué a una versión más sucinta de lo mismo: (pid = fork)? Process.detach (pid): exec ("foo") – kch

+0

Sí. Eso funciona. –

+0

@MatthewFlaschen En caso de que no contenga 'exec', ¿no necesito una' exit' en el código hijo para que el proceso zombie no dure? – oldergod

2

Separación $$ no estaba bien. De p. 348 del Pico (2nd Ed):

$$ Fixnum El número de proceso del programa que se está ejecutando.[R/O]

En esta sección, "Variables y constantes" en el capítulo "Lenguaje Ruby", es muy útil para decodificar varios rubí cortos $ constantes - sin embargo, la edición en línea (la primera

Así lo que en realidad estaba haciendo era separar el programa de sí mismo, no de su hijo. Como otros han dicho, la manera correcta de separar al niño es usar el pid del niño devuelto de fork().

+0

Algo borked tu respuesta. De todos modos, tengo la tercera edición de la piqueta. Entonces, $$ no está bien, de hecho. ¿Pero por qué funcionó eso de todos modos? – kch

+2

No funciona bien si llama a desconexión en $$. Pero, de nuevo, desapegarse es solo para evitar los procesos zombies. No es necesario ejecutar un trabajo en segundo plano. –

2

Las otras respuestas son buenas si está seguro de que desea separar el proceso secundario. Sin embargo, si no le importa, o preferiría mantener el proceso hijo adjunto (p. usted está lanzando sub-servidores/servicios para una aplicación web), entonces usted puede tomar ventaja de la siguiente forma abreviada

fork do 
    exec('whatever --option-flag') 
end 

Proporcionar un bloque le dice tenedor para ejecutar ese bloque (y sólo ese bloque) en el proceso hijo, mientras continúa en el padre.

0

encontré que las respuestas anteriores rompieron mi terminal y arruinaron la salida. esta es la solución que encontré.

system("nohup ./test.sh &") 

por si alguien tiene el mismo problema, mi objetivo era iniciar sesión en un servidor ssh y luego mantener ese proceso que se ejecuta de forma indefinida. entonces test.sh es esto

#!/usr/bin/expect -f 
spawn ssh host -l admin -i cloudkey 
expect "pass" 
send "superpass\r" 
sleep 1000000 
Cuestiones relacionadas