2009-08-18 11 views
9

単純なWebサーバーを実行しているサブプロセスを起動するアプリケーションを作成しています。私はNSTaskを使用しており、パイプで通信しています。しかし、私のプログラムがクラッシュした場合、サブプロセスは生き残ります。次回にアプリケーションを起動すると、古いサブプロセスと新しいサブプロセスの間に競合が発生します。所有アプリが死んだときにサブプロセスが確実に消滅するような方法はありますか?サブプロセスがココアで死んでいることを確認してください

+0

は、あなたのプログラムがクラッシュしない作ってみましたがありますか? :D – kubi

+0

ここでAtlasについて考えています^^ –

+0

LaunchdでWebサーバーを実行してみることができます。少なくともそのように、あなたのアプリケーションがクラッシュして再起動したとき、Launchdはサーバーが既に起動しているので、シャットダウンして再起動することができます。 –

答えて

1

アプリケーションデリゲートは

- (void)applicationWillTerminate:(NSNotification *)aNotification 

メッセージを実装し、そこNSTaskを終了することができます。ただし、クラッシュ中にこの代理人が呼び出されることは保証されていません。

あなたが取ることができる2つの追加手順:

  • シャットダウン作成上のディスクにサブプロセスのPIDを書き留め、通常のシャットダウン時に、それを除去することにより、新しい親プロセスの起動時に既存の、孤立したサブプロセス(ときどき最も安全な行動ではない)。
  • NSPipeのエンドポイントが特定の時間(ハートビートのようなもの)のデータを送信しなかった場合、サブプロセスをシャットダウンします。
0

更新:これを正しくチェックするようになったので、正しく動作しません。プロセスグループを設定しようとすると、このエラーで失敗します。

EPERM "要求されたプロセスの実効ユーザーIDが呼び出し元のユーザーIDと異なり、プロセスが呼び出しプロセスの子孫ではありません。"

この問題の詳細最近のスレッドが、私の知る限り、私は私のアプリでCocoadevのロバート・Pointonの提案を試してみた

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


を言うことができるように簡単な解決策があります。私はまだそれをテストすることに慣れていない。

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文書化・複雑だが、この一般的なシナリオを処理する方法がありません。私はAppleがちょうどバックグラウンド・プロセスを実行するための「母船-承認」の方法を確認しない理由を知らないが、何でも...私の解決策がある...

  1. 起動シェルスクリプトをNSTask経由し、それをあなたが必要とするすべての変数を渡します。 また...ターンでは、スクリプト内部からあなたのサブプロセスを起動

  2. $ 1、$ 2等を介して、あなたのスクリプトでこれらを読むなどint masterPID = [[NSProcessInfo processInfo] processIdentifier];を経由して、あなたの親プロセスのPIDに渡す

  3. スクリプト内の親プロセスであるサブプロセスを監視します。

これは、「子供たちに目を離さない..」することができます、そしてparentcide(または恐ろしい車の事故)の悲しい出来事に...二重の目的を果たす - キルオフゾンビ孤児を。その後、あなたは(あなたがシェルスクリプト)自分で引き金を引くと、あなたがを決して存在しなかったかのように、あなたのプロセステーブルが.. きれいになります。ブロックされたポートはなく、再起動時に競合はなく、アプリストアの拒否はありません。 Lemmeがこれを助けるかどうかを知る!

更新:私は、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; 
} 
関連する問題