2009-08-18 133 views
9

我正在編寫一個啓動運行簡單Web服務器的子進程的應用程序。我正在使用NSTask並用管道與它進行通信,並且一切看起來都很不錯。但是,如果我的程序崩潰了,子進程將保持活動狀態,下次啓動應用程序時,舊子進程和新子進程之間會發生衝突。有沒有什麼方法可以確保子進程在擁有的應用程序死亡時死亡?確保子進程在Cocoa中死亡

+0

您是否嘗試過讓你的程序不會崩潰? :D – kubi 2009-08-18 21:00:06

+0

我正在考慮Atlas這裏^^ – 2010-01-25 00:19:28

+0

您可以嘗試在Launchd中運行Web服務器。至少這樣,當你的應用程序崩潰並重新啓動時,Launchd會告訴你服務器已經在運行,所以你可以關閉它並重新啓動它,如果你想的話。 – 2012-01-07 22:14:58

回答

1

您的應用程序委託可以實現

- (void)applicationWillTerminate:(NSNotification *)aNotification 

消息,並終止NSTask那裏。但是,不能保證在發生崩潰時這個代表將被調用。你可以採取

兩個額外的步驟:

  • 關機推出一個新的父進程的過程中現有的,孤立子寫下磁盤的PID上創建子進程和正常的關機過程中移除(有時不是最安全的行爲)。
  • 如果NSPipe的終點沒有在特定時間內發送數據(如心跳),則關閉子流程。
0

更新:現在,我去檢查這適當它不起作用。嘗試設置進程組失敗並出現此錯誤;

EPERM「請求進程的有效用戶標識與調用方的有效用戶標識不同,進程不是調用進程的後代。」

有關於這個問題,但沒有簡單的解決方案更近的線程,據我可以告訴

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


我已經試過由羅伯特·普安頓上Cocoadev建議在我的應用程序。儘管如此,我還沒有去測試它。

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

我們的想法是,設置任務的進程組是相同的是啓動任務(注意:下面的代碼基本上是從上方螺紋擡起)的方法的。

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

無上述作品......甚至launchd在所有它的crappily詳細記錄,複雜性的方式來處理這種常見的場景。我不知道爲什麼蘋果不只是做一個「母船批准」的方式來運行後臺進程,但無論..我的解決辦法是...

  1. 啓動一個shell腳本,通過NSTask,並通過它,你需要的任何變量。 另外在你的父進程的PID通過int masterPID = [[NSProcessInfo processInfo] processIdentifier];等傳遞通過$ 1,$ 2,等等。閱讀這些腳本中的

  2. 反過來,從腳本內啓動你的子過程..

  3. 監視腳本中的父進程的子進程

這有兩個目的..它使您能夠「在孩子們保持眼睛..」,並在parentcide(或可怕的車禍)的不幸的事件 - 殺客殭屍孤兒。然後,你自己拉起扳機(,你是shell腳本),你的工藝表將被清理乾淨。就好像你從未存在過。沒有阻塞的端口,在重新啓動時沒有衝突,沒有應用商店拒絕。讓我知道這是否有幫助!

更新:我做了一個Xcode模板/守護進程/項目/無論如何伎倆。檢查出來.. mralexgray/Infanticide.

1

以下代碼示例應該可以幫到你。

它是從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; 
}