2009-08-18 16 views
9

Ich schreibe eine Anwendung, die einen Subprozess startet, auf dem ein einfacher Webserver läuft. Ich benutze NSTask und kommuniziere damit mit Pipes, und alles scheint mehr oder weniger gut zu sein. Wenn jedoch mein Programm abstürzt, bleibt der Subprozess bestehen und beim nächsten Start der App gibt es einen Konflikt zwischen dem alten und dem neuen Subprozess. Gibt es eine Möglichkeit sicherzustellen, dass Subprozesse sterben, wenn die Eigentümer App stirbt?Stellen Sie sicher, dass ein Subprozess in Cocoa ungültig ist

+0

Haben Sie versucht, Ihr Programm nicht abstürzen zu machen? : D – kubi

+0

Ich denke über Atlas hier ^^ –

+0

Sie könnten versuchen, den Webserver in Launched auszuführen. Zumindest wenn Ihre App abstürzt und Sie neu starten, wird Launchd Ihnen sagen, dass der Server bereits läuft, damit Sie ihn herunterfahren und neu starten können, wenn Sie möchten. –

Antwort

1

Ihre Anwendung delegieren können die

- (void)applicationWillTerminate:(NSNotification *)aNotification 

Nachricht, implementieren und die NSTask dort beenden. Es ist jedoch nicht garantiert, dass dieser Delegat während eines Absturzes aufgerufen wird.

Zwei zusätzliche Schritte können Sie ergreifen:

  • Shutdown eine bestehende, verwaiste subprocess bei der Einführung eines neuen Eltern-Prozess von unten auf die Festplatte des subprocess bei der Erstellung des PID-Schreiben und es während des normalen Herunterfahren entfernen (manchmal nicht das sicherste Verhalten).
  • Beenden Sie den Subprozess, wenn der Endpunkt des NSPipes für eine bestimmte Zeit keine Daten gesendet hat (etwa wie ein Heartbeat).
0

UPDATE: Jetzt, wo ich gehe, um dies richtig zu überprüfen, funktioniert es nicht. Der Versuch, die Prozessgruppe festzulegen, schlägt mit diesem Fehler fehl.

EPERM "Die effektive Benutzer-ID des angeforderten Prozesses unterscheidet sich von der des Anrufers, und der Prozess ist kein Nachkomme des aufrufenden Prozesses."

Es ist ein neuerer Thread zu diesem Thema, aber keine einfachen Lösung, soweit ich

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


ich einen Vorschlag von Robert Pointon auf CocoaDev in meiner App versucht habe, berichten. Ich habe es aber noch nicht getestet.

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

Die Idee ist es, die Prozeßgruppe der Aufgabe zu setzen dieselbe wie die des Verfahrens zu sein, die die Aufgabe startet (Anmerkung: Der folgende Code grundsätzlich aus dem Thread oben angehoben wird).

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

Keine der oben genannten Arbeiten ... Nicht einmal launchd in allem ist es crappily dokumentierte Komplexität hat einen Weg, um dieses gemeinsame Szenario zu behandeln. Ich weiß nicht, warum Apple nicht nur eine „Mutterschiff-approved“ Platz machen, Hintergrundprozesse zu laufen, aber was auch immer .. meine Lösung ist ...

  1. Starten Sie ein Shell-Skript über NSTask und geben sie alle Variablen, die Sie benötigen. geben Auch in Ihrer Mutter Prozess PID über int masterPID = [[NSProcessInfo processInfo] processIdentifier]; usw. Lesen Sie diese in Ihrem Skript über $ 1, $ 2 usw.

  2. wiederum starten Sie Ihre Subprozesse von im Skript ..

  3. Überwachen Sie den Unterprozess AND Ihren übergeordneten Prozess innerhalb des Skripts.

Dies dient einem doppelten Zweck .. es Ihnen ermöglicht, „auf die Kinder im Auge zu behalten ..“, und in dem traurigen Ereignis von parentcide (oder schrecklichen Autounfall) - Kill-off der Zombie-Waisen. Dann ziehen Sie den Auslöser auf sich selbst (Sie sind das Shell-Skript) und Ihre Prozesstabelle wird sauber sein. als ob Sie nie existiert. Keine blockierten Ports, keine Konflikte beim Relaunch, keine App-Store-Ablehnungen. Lemme wissen, ob das hilft!

Update: Ich habe eine Xcode Vorlage/Daemon/Projekt/was auch immer das macht den Trick. Überprüfen Sie es heraus. mralexgray/Infanticide.

1

Das folgende Codebeispiel sollte Ihnen helfen.

es aus here entlehnt ist,

#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; 
} 
Verwandte Themen