%20%E3%82%92%E4%BD%BF%E3%81%84%E5%A7%8B%E3%82%81%E3%81%9F%20%C2%ABsh%C2%BB%20%E3%81%AF%E3%82%BE%E3%83%B3%E3%83%93%E3%81%AB%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%99.png)
半日を過ごしましたが、execl
通話で始まったダッシュがなぜゾンビに変わったのかはまだわかりませんでした。
以下は最小限のテストケースです。私は子をフォークしてstdをコピーします。[入力、出力、エラー]記述子と開始シェン。
#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()
)それを見てください(これPID変数を使用してpsを使用してそのプロセスを表示すると、次のような結果が毎回表示されます。
2794 pts/10 00:00:00 sh <defunct>
つまり、ゾンビ
答え1
wait
あなたは子プロセスにいないので些細なことですが、ゾンビを残しています。
無意味な方法でSTDINを設定したので、シェルはすぐに終了します。pipe
一方向通信チャネルを返します。到着するwrite
とからpipefd[1]
戻ります。シェルがパイプ(STDIN)の書き込み側で読み取ろうとする一連の呼び出しを実行します。read
pipefd[0]
dup2
列挙型の数字を変更すると、シェルが永久に停止しますread
。これはおそらくあなたが望むものではないかもしれませんが、パイプを通してシェルがそれ自体に接続されているときに期待できるすべてです。
親プロセスでシェルを使用したいと仮定すると、それを2回呼び出す必要がありますpipe
(親プロセスの両方)。ユーザーが書き込むパイプの1つ(シェルはstdinから読み取る)と、シェルが書き込む他のパイプ(stdout / stderr)とそれを読みます。または必要に応じてsocketpair
。
答え2
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
コードの値)の処理を検討する必要があります。