4

fork()を使用してCでバックグラウンドプロセスを作成しています。

これらのプロセスの1つを作成したら、そのpidを配列に追加して、バックグラウンドプロセスを追跡できるようにします。

    pid = fork();

    if(pid == -1) 
    {
        printf("error: fork()\n");
    }
    else if(pid == 0) 
    {
        execvp(*args, args);
        exit(0);
    }
    else  
    {
        // add process to tracking array
        addBGroundProcess(pid, args[0]);
    }

ゾンビを刈り取るためのハンドラーがあります

void childHandler(int signum) 
{ 
    pid_t pid; 
    int status; 

    /* loop as long as there are children to process */ 
    while (1) { 

       /* get zombie pids */ 
       pid = waitpid(-1, &status, WNOHANG); 

       if (pid == -1)
       { 
           if (errno == EINTR)
           { 
               continue; 
           } 

           break; 
       } 
       else if (pid == 0)
       { 
           break; 
       } 

       /* Remove this child from tracking array */ 
       if (pid != mainPid)
            cleanUpChild(pid);
    }    
}

バックグラウンドプロセスを作成すると、ハンドラーが実行され、addBGroundProcessを呼び出す前に子をクリーンアップしようとします。

私はemacs&のようなコマンドを使用していますが、すぐに終了するべきではありません。

私は何が欠けていますか?

ありがとう。

4

3 に答える 3

4

そうです、競合状態があります。SIGCHLDこの機能を使用して配信をブロックすることをお勧めしますsigprocmask。新しい PID をデータ構造に追加したら、信号のブロックを再度解除します。シグナルがブロックされると、そのシグナルが受信された場合、カーネルはそのシグナルを配信する必要があることを記憶し、シグナルのブロックが解除されると配信されます。

具体的には、次のようになります。

sigset_t mask, prevmask;

//Initialize mask with just the SIGCHLD signal
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);

sigprocmask(SIG_BLOCK, &mask, &prevmask); /*block SIGCHLD, get previous mask*/
pid = fork();

if(pid == -1) 
{
    printf("error: fork()\n");
}
else if(pid == 0) 
{
    execvp(*args, args);
    exit(0);
}
else  
{
    // add process to tracking array
    addBGroundProcess(pid, args[0]);

    // Unblock SIGCHLD again
    sigprocmask(SIG_SETMASK, &prevmask, NULL);
}

execvpまた、失敗する可能性もあると思います。&(この場合は発生していなくても、一般的にこれを処理するのは良いことです。) それがどのように実装されているかによって異なりますが、コマンドの最後に a を付けて取得することは許可されていないと思います。バックグラウンドで実行します。emacsいずれにしても、この場合は単独で実行することをお勧めします&。コマンド ラインの最後に配置することは、シェルによって提供される機能です。

編集: 現在のターミナル セッションで emacs を実行したくないというコメントを見ました。正確には、別の X11 ウィンドウでどのように実行しますか? もしそうなら、それを達成する他の方法があります。

execvpの失敗を処理するかなり簡単な方法は、次のようにすることです。

    execvp(*args, args);
    perror("execvp failed");
    _exit(127);
于 2010-10-03T01:55:49.450 に答える
0

あなたのコードは、フォークした子プロセスの終了をキャッチするだけです。これは、別のプロセスがその子によって最初にフォークされなかったと言っているわけではありません。あなたの場合のemacsは、何らかの理由でそれ自体で別の fork() を実行してから、最初のプロセスを終了できるようにしていると推測しています(これはデーモンが行うトリックです)。

setsid() 関数も検討する価値があるかもしれませんが、それを確認するために自分でコードを作成しない限り、それがここで関連しているかどうかはわかりません。

于 2010-10-03T01:13:34.760 に答える
0

&バックグラウンド プロセスを実行するためにシェルを使用しないでください。そうすれば、彼らはあなたが追跡して待つことができない孫として出てきます. 代わりに、シェルが独自のコードでバックグラウンド プロセスを実行するために行うことを模倣するか、端末を閉じて (またはむしろ stdin/out/err) /dev/null、子プロセスの代わりに開くのと同じように機能する可能性があります。彼らは端末に書き込もうとしたり、端末を制御したりしようとしません。

于 2010-10-03T01:59:38.713 に答える