web-dev-qa-db-ja.com

execl()で起動された«sh»はゾンビになります

私は半日を過ごしましたが、それでも理解できませんでした。execl呼び出しでダッシュが起動されるのはなぜゾンビになるのですか。

以下は最小限のテストケースです—私は子供をフォークし、std [in、out、err]記述子の複製を作成し、shを起動します。

_#include <cstdio>
#include <fcntl.h>
#include <cstring>
#include <stdlib.h>
#include <cerrno>
#include <unistd.h>

int main() {
    int pipefd[2];
    enum {
        STDOUT_TERM = 0,
        STDIN_TERM  = 1
    };
    if (pipe(pipefd) == -1) { //make a pipe
        perror("pipe");
        return 0;
    }
    pid_t pid = fork();
    if (pid == 0)
    {// Child
        dup2(pipefd[STDIN_TERM], STDIN_FILENO);
        dup2(pipefd[STDOUT_TERM], STDOUT_FILENO);
        dup2(pipefd[STDOUT_TERM], STDERR_FILENO);
        execl("/bin/sh","sh", (char*)NULL);
        // Nothing below this line should be executed by child process. If so, print err
        perror("For creating a Shell process");
        exit(1);
    }
    __asm("int3");
    puts("Child launched");
}
_

デバッガーで起動し、ブレークポイントのある行でputs() call)pid変数を確認してから、 psによるプロセスに従って、私は毎回次のようなものを取得します

_2794 pts/10   00:00:00 sh <defunct>
_

つまりそのゾンビ

3
Hi-Angel

子プロセスでwaitしなかったので、些細なことにゾンビを残しています。

STDINを無意味な方法で設定したため、シェルはすぐに終了します。 pipeは一方向の通信チャネルを返します。 writeからpipefd[1]に移動し、readpipefd[0]から戻します。シェルがパイプの書き込み側から読み取り(STDIN)を試行するように誘導する、大量のdup2呼び出しを実行しました。

列挙型の数値を交換すると、シェルはreadに永久に置かれます。それはおそらくあなたが望むものではありませんが、シェルをそれ自体にパイプしたときに期待できることはそれだけです。

親プロセスからシェルを使用しようとしていると仮定すると、pipeを2回(および両方とも親で)呼び出す必要があります。書き込み先のパイプの1つ(およびシェルはstdinで読み取ります)もう一方のシェルは(stdout/stderr)に書き込み、そこから読み取ります。または、必要に応じて、代わりにsocketpairを使用してください。

7
derobert

SIGCHLDシグナルをキャッチし、wait()システムコールでゾンビプロセスを「刈り取る」必要があります。シグナルハンドラー関数を追加するためのプログラムへのコードの追加はほぼ最小限であり、SIGCHLDハンドラーとして次のように設定します。

_#include <cstdio>
#include <fcntl.h>
#include <cstring>
#include <stdlib.h>
#include <cerrno>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

void sighandler(int signal_number);

void sighandler(int signo) {
    if (signo == SIGCHLD) {
            int status;
            (void)wait(&status);
    }
}


int main() {
    int pipefd[2];
    enum {
        STDOUT_TERM = 0,
        STDIN_TERM  = 1
    };
    signal(SIGCHLD, sighandler);
    pid_t pid = fork();
_

ほぼ確実に、signal()システムコールの戻りステータスを確認し、子の終了ステータス(シグナルハンドラコードのstatusの値)の処理を検討する必要があります。

2
Bruce Ediger