5

私は子供にフォークして何らかの仕事をさせるプログラムを持っていますが、現時点では一度に1人の子供しかやっていません。子が終了するのを待つために使用wait()していますが、何かを行う必要がありますかSIGCHLD(ハン​​ドラーを無効にするなど)?

私の状況では、 in の値を取得しているEINTRためerrno、 mask が必要だと思いますSIGCHLD

大まかに言えば、これはプログラムです:

  • 引数を読む
  • for(やることリスト)
  • fork()
  • 子供の場合は、execlp()プログラムを動作させる
  • 親の場合wait()、子が終了する場合
  • 子が終了すると、親は次の作業項目にループします
4

2 に答える 2

8

仮説

POSIXは次のように述べていますSIG_IGN(厳密には XSI 拡張ノートの下で):

SIGCHLD シグナルのアクションが SIG_IGN に設定されている場合、呼び出しプロセスの子プロセスは、終了時にゾンビ プロセスに変換されません。呼び出しプロセスがその後その子を待機し、プロセスにゾンビ プロセスに変換された待機していない子がない場合、そのプロセスはすべての子が終了するまでブロックし、wait()、waitid()、および waitpid() は、失敗し、errno を [ECHILD] に設定します。

の説明で<signal.h>は、 のデフォルトの信号処理SIGCHLDSIG_IGN「無視」(コード「I」) であると述べています。ただし、SIG_DFL信号を「無視」するのは動作です。信号は生成されません。これは動作とは異なりSIG_IGNます。

したがって、シグナル ハンドラーを設定する必要はありませんが、プロセスに関する情報を取得するべきではありません。

デモコード

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static siginfo_t sig_info;
static volatile sig_atomic_t sig_num;
static void *sig_ctxt;

static void catcher(int signum, siginfo_t *info, void *vp)
{
    sig_num = signum;
    sig_info = *info;
    sig_ctxt = vp;
}

static void set_handler(int signum)
{
    struct sigaction sa;
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = catcher;
    sigemptyset(&sa.sa_mask);

    if (sigaction(signum, &sa, 0) != 0)
    {
        int errnum = errno;
        fprintf(stderr, "Failed to set signal handler (%d: %s)\n", errnum, strerror(errnum));
        exit(1);
    }
}

static void prt_interrupt(FILE *fp)
{
    if (sig_num != 0)
    {
        fprintf(fp, "Signal %d from PID %d\n", sig_info.si_signo, (int)sig_info.si_pid);
        sig_num = 0;
    }
}

static void five_kids(void)
{
    for (int i = 0; i < 5; i++)
    {
        pid_t pid = fork();
        if (pid < 0)
            break;
        else if (pid == 0)
        {
            printf("PID %d - exiting with status %d\n", (int)getpid(), i);
            exit(i);
        }
        else
        {
            int status = 0;
            pid_t corpse = wait(&status);
            printf("Child: %d; Corpse: %d; Status = 0x%.4X\n", pid, corpse, (status & 0xFFFF));
            prt_interrupt(stdout);
            fflush(0);
        }
    }
}

int main(void)
{
    printf("SIGCHLD set to SIG_IGN\n");
    signal(SIGCHLD, SIG_IGN);
    five_kids();
    printf("SIGCHLD set to catcher()\n");
    set_handler(SIGCHLD);
    five_kids();
    return(0);
}

このfflush(0);呼び出しにより、標準出力 (特に) が確実にフラッシュされます。これは、サンプル プログラムの出力が別のプログラムにパイプされる場合に重要です。

出力例

コード例の出力は理論と一致していますが、少し解釈が必要です。

SIGCHLD set to SIG_IGN
PID 4186 - exiting with status 0
SIGCHLD set to SIG_IGN
Child: 4186; Corpse: -1; Status = 0x0000
PID 4187 - exiting with status 1
Child: 4187; Corpse: -1; Status = 0x0000
PID 4188 - exiting with status 2
Child: 4188; Corpse: -1; Status = 0x0000
PID 4189 - exiting with status 3
Child: 4189; Corpse: -1; Status = 0x0000
PID 4190 - exiting with status 4
Child: 4190; Corpse: -1; Status = 0x0000
SIGCHLD set to catcher()
PID 4191 - exiting with status 0
SIGCHLD set to catcher()
Child: 4191; Corpse: -1; Status = 0x0000
Signal 20 from PID 4191
Child: 4192; Corpse: 4191; Status = 0x0000
PID 4192 - exiting with status 1
Child: 4193; Corpse: 4192; Status = 0x0100
Signal 20 from PID 4192
PID 4193 - exiting with status 2
Child: 4194; Corpse: 4193; Status = 0x0200
Signal 20 from PID 4193
PID 4194 - exiting with status 3
Child: 4195; Corpse: 4194; Status = 0x0300
Signal 20 from PID 4194
PID 4195 - exiting with status 4

出力の最初のセクションは理論と完全に一致しています。呼び出し元のコードは、待機する死んだ子 (左) がないことを除いて、死んだ子に関する情報を取得しません。

出力の 2 番目のセクションも理論と一致しますが、親の前に子が実行されていないように見えるため、親wait()からの最初のセクションには待機する死んだ子がなく、-1 が返されます。後続の呼び出しでさまざまな子が取得されますが、1 つは待機せずに脱出します (ただし、死体はシステムによってクリーンアップされます)。waitpid()次のようなコードを使用pid_t corpse = waitpid(pid, &status, 0);すると、各子を順番に待機します。

この解説の一部は、上記の「無視」に関する修正されたコメントに照らして修正が必要な場合があります。TBD — 今は時間切れです。

3 GHz Intel Core 2 Duo、GCC 4.6.0、64 ビット コンパイル上の Mac OS X 10.8.4:

gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
    -Wold-style-definition sigchld.c -o sigchld 

SIGINTこれは、との動作をテストするために、いくつかの既存のコードを手早く適応させたものpause()です。余計な素材が入っている可能性があります。wait()シグナル ハンドラで、またはその親戚の 1 つを呼び出す必要はありません。ただし、必要に応じてそうすることができます。

于 2013-08-26T07:12:29.310 に答える
3

分岐した子の終了ステータスを気にしない場合は、無視できSIGCHLDます。

signal(SIGCHLD, SIG_IGN);

終了ステータスが気になる場合は、シグナル ハンドラーをインストールし、シグナル ハンドラーwaitpid()内で呼び出しを実行して、終了した子プロセスから終了ステータス値を取得できます。この呼び出しは、終了した子がなくなるまでループする必要があります。-1最初のパラメーターとWNOHANG最後のパラメーターで使用して、ブロックされていない方法で子を待機します。0 を返す場合waitpid()、呼び出しは成功したが、待機する子がもうないことを意味します。

void sigchld_handler (int sig) {
    assert(sig == SIGCHLD);
    int status;
    pid_t child;
    while ((child = waitpid(-1, &status, WNOHANG)) > 0) {
        /*...do something with exited child's status */
    }
}

/*...*/
signal(SIGCHLD, sigchld_handler);
于 2013-08-26T06:31:51.670 に答える