2009-08-18 21 views
9

Estoy escribiendo una aplicación que inicia un subproceso que ejecuta un servidor web simple. Estoy usando NSTask y comunicándome con él con tuberías, y todo parece más o menos correcto. Sin embargo, si mi programa falla, el subproceso se deja con vida y la próxima vez que inicie la aplicación hay un conflicto entre el antiguo subproceso y el nuevo. ¿Hay alguna manera de asegurar que los subprocesos mueran cuando la aplicación propietaria muere?Asegurar que un subproceso está muerto en Cocoa

+0

¿Ha intentado hacer su programa se cuelga? : D – kubi

+0

Estoy pensando en Atlas aquí ^^ –

+0

Puede intentar ejecutar el servidor web en Launchd. Al menos de esa manera, cuando su aplicación falla y se reinicia, Launchd le dirá que el servidor ya se está ejecutando, por lo que puede cerrarlo y reiniciarlo si lo desea. –

Respuesta

1

Su delegado aplicación puede poner en práctica el mensaje

- (void)applicationWillTerminate:(NSNotification *)aNotification 

, y terminar el NSTask allí. Sin embargo, no se garantiza que durante un choque se llame a este delegado.

dos pasos adicionales que puede tomar:

  • apagado una ya existente, subproceso huérfanos durante el lanzamiento de un nuevo padre-proceso por escribir en el disco el PID del subproceso de creación y retirarlo durante el cierre normal de (a veces no es el comportamiento más seguro).
  • Apague el subproceso si el punto final de la NSPipe no envió datos durante un período de tiempo específico (algo así como un latido del corazón).
0

ACTUALIZACIÓN: Ahora que voy a comprobar esto correctamente, no funciona. Intentando configurar el grupo de proceso falla con este error;

EPERM "La identificación de usuario efectiva del proceso solicitado es diferente de la de la persona que llama y el proceso no es un descendiente del proceso de llamada."

hay un hilo más reciente sobre este tema, pero hay una solución fácil por lo que yo puedo decir

http://www.omnigroup.com/mailman/archive/macosx-dev/2009-March/062164.html


He intentado una sugerencia de Robert Pointon en Cocoadev en mi aplicación. Aunque aún no llegué a probarlo.

http://www.cocoadev.com/index.pl?NSTaskTermination

La idea es establecer el grupo de procesos de la tarea a ser el mismo que el del proceso que inicia la tarea (nota: el código de abajo es, básicamente, levantada del hilo anterior).

pid_t group = setsid(); 
    if (group == -1) { 
     group = getpgrp(); 
    } 

    [task launch]; 


if (setpgid([task processIdentifier], group) == -1) { 
    NSLog(@"unable to put task into same group as self"); 
    [task terminate]; 
} else { 
// handle running task 
} 
2

Nada de lo anterior funciona ... Ni siquiera launchd en toda su crappily-documentada complejidad tiene una manera de manejar esta situación común. No sé por qué Apple no se limita a hacer una forma "nodriza aprobados" para ejecutar procesos en segundo plano, pero lo que sea .. mi solución es ...

  1. lanzamiento a través de un script de shell NSTask y pasarlo cualquier variable que necesita. también pasan en su proceso padre PID través int masterPID = [[NSProcessInfo processInfo] processIdentifier]; etc. Lea estos en su secuencia de comandos a través de $ 1, $ 2, etc.

  2. A su vez, poner en marcha sus procesos parciales de dentro de la secuencia de comandos ..

  3. Supervise el subproceso Y su proceso primario dentro del script.

Esto tiene un doble propósito .. que le permite "mantener un ojo en los niños ..", y en el triste caso de parentcide (o un accidente automovilístico horrible) - Kill-off los huérfanos zombi. Luego, aprieta el gatillo usted mismo (siendo el script de shell) y su tabla de procesos estará limpia .. como si nunca hubiera existido. Sin puertos bloqueados, sin conflictos en el relanzamiento, sin rechazos de la tienda de aplicaciones. ¡Déjame saber si esto ayuda!

Actualización: Hice una plantilla/daemon/proyecto de Xcode/lo que sea que haga el truco. Verifíquelo ... mralexgray/Infanticide.

1

El siguiente ejemplo de código debería ayudarlo.

que es tomado de here,

#include <CoreFoundation/CoreFoundation.h> 
#include <unistd.h> 
#include <sys/event.h> 
static void noteProcDeath(CFFileDescriptorRef fdref, CFOptionFlags callBackTypes, void *info) { 
    struct kevent kev; 
    int fd = CFFileDescriptorGetNativeDescriptor(fdref); 
    kevent(fd, NULL, 0, &kev, 1, NULL); 
    // take action on death of process here 
    printf("process with pid '%u' died\n", (unsigned int)kev.ident); 
    CFFileDescriptorInvalidate(fdref); 
    CFRelease(fdref); // the CFFileDescriptorRef is no longer of any use in this example 
} 
// one argument, an integer pid to watch, required 
int main(int argc, char *argv[]) { 
    if (argc < 2) exit(1); 
    int fd = kqueue(); 
    struct kevent kev; 
    EV_SET(&kev, atoi(argv[1]), EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT, 0, NULL); 
    kevent(fd, &kev, 1, NULL, 0, NULL); 
    CFFileDescriptorRef fdref = CFFileDescriptorCreate(kCFAllocatorDefault, fd, true, noteProcDeath, NULL); 
    CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack); 
    CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0); 
    CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode); 
    CFRelease(source); 
    // run the run loop for 20 seconds 
    CFRunLoopRunInMode(kCFRunLoopDefaultMode, 20.0, false); 
    return 0; 
} 
Cuestiones relacionadas