私は半日を過ごしましたが、それでも理解できませんでした。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>
_
つまりそのゾンビ
子プロセスでwait
しなかったので、些細なことにゾンビを残しています。
STDINを無意味な方法で設定したため、シェルはすぐに終了します。 pipe
は一方向の通信チャネルを返します。 write
からpipefd[1]
に移動し、read
をpipefd[0]
から戻します。シェルがパイプの書き込み側から読み取り(STDIN)を試行するように誘導する、大量のdup2
呼び出しを実行しました。
列挙型の数値を交換すると、シェルはread
に永久に置かれます。それはおそらくあなたが望むものではありませんが、シェルをそれ自体にパイプしたときに期待できることはそれだけです。
親プロセスからシェルを使用しようとしていると仮定すると、pipe
を2回(および両方とも親で)呼び出す必要があります。書き込み先のパイプの1つ(およびシェルはstdinで読み取ります)もう一方のシェルは(stdout/stderr)に書き込み、そこから読み取ります。または、必要に応じて、代わりにsocketpair
を使用してください。
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
の値)の処理を検討する必要があります。