2009-08-18 11 views
9

Sto scrivendo un'applicazione che avvia un sottoprocesso che esegue un server Web semplice. Sto usando NSTask e comunico con esso con le pipe, e tutto sembra più o meno bello. Tuttavia, se il mio programma si arresta in modo anomalo, il sottoprocesso viene lasciato in vita e al prossimo avvio dell'app c'è un conflitto tra il vecchio sottoprocesso e il nuovo. C'è un modo per garantire che i sottoprocessi muoiano quando l'app proprietaria muore?Verificare che un sottoprocesso sia morto in Cocoa

+0

Avete provato a fare il vostro programma va in crash? : D – kubi

+0

Sto pensando ad Atlas qui ^^ –

+0

Si potrebbe provare a eseguire il server Web in Launchd. Almeno in questo modo, quando la tua app si arresta e viene riavviata, Launchd ti dirà che il server è già in esecuzione, quindi puoi spegnerlo e riavviarlo se vuoi. –

risposta

1

Il delegato applicazione può implementare il messaggio

- (void)applicationWillTerminate:(NSNotification *)aNotification 

, e terminare il NSTask lì. Tuttavia, non è garantito che durante un arresto, questo delegato venga chiamato.

due ulteriori passaggi si può prendere:

  • Shutdown uno esistente, sottoprocesso orfani durante il lancio di un nuovo genitore-processo scrivendo su disco il PID del sottoprocesso sulla creazione e la rimozione durante l'arresto normale (a volte non è il comportamento più sicuro).
  • Arresta il sottoprocesso se il punto finale dell'NSPipe non ha inviato dati per un intervallo di tempo specifico (qualcosa come un heartbeat).
0

AGGIORNAMENTO: Ora che vado a controllare correttamente, non funziona. Cercando di impostare il gruppo di processi non riesce con questo errore;

EPERM "L'ID utente effettivo del processo richiesto è diverso da quello del chiamante e il processo non è un discendente del processo di chiamata."

C'è un filo più recente su questo tema, ma non facile soluzione, per quanto posso dire

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


Ho provato un suggerimento di Robert Pointon sul Cocoadev nella mia app. Non ho ancora provato a provarlo.

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

L'idea è di impostare il gruppo processo del compito di essere la stessa di quella del processo che avvia l'attività (nota: il codice di seguito viene sostanzialmente sollevata dal filo sopra).

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

Nessuna delle opere di cui sopra ... Nemmeno launchd in tutta la sua crappily-documentata complessità ha un modo per gestire questo scenario comune. Non so il motivo per cui Apple non si limita a fare un modo "madre-approvati" per eseguire i processi in background, ma qualunque cosa .. la mia soluzione è ...

  1. lancio uno script shell tramite NSTask e passarlo tutte le variabili necessarie. passano anche nel processo padre PID via int masterPID = [[NSProcessInfo processInfo] processIdentifier]; ecc Read questi nello script tramite $ 1, $ 2, ecc

  2. A sua volta, avviare il sottoprocessi dal all'interno dello script ..

  3. Monitorare sia il sottoprocesso AND processo genitore all'interno dello script.

Questo serve un duplice scopo .. si consente di "tenere d'occhio i bambini ..", e nella triste caso di parentcide (o incidente d'auto orribile) - kill-off gli orfani zombie. Quindi, premi il grilletto su te stesso (tu sei lo script di shell) e la tabella del processo sarà pulita .. come se non esistessi mai. Nessuna porta bloccata, nessun conflitto sul rilancio, nessun rifiuto dell'app store. Fammi sapere se questo aiuta!

Aggiornamento: Ho creato un modello/demone Xcode/progetto/qualsiasi cosa faccia il trucco. Dai un'occhiata .. mralexgray/Infanticide.

1

Il seguente codice di esempio dovrebbe aiutarti.

è preso in prestito da 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; 
} 
Problemi correlati