我花了半天时间,还是想不通,为什么用execl
call启动的dash就变成了僵尸。
下面是一个最小的测试用例 - 我只是分叉一个孩子,复制 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
在你的子进程上。
你的 shell 会立即退出,因为你以一种无意义的方式设置了它的 STDIN。pipe
返回单向通信通道。你write
到,pipefd[1]
它read
从 回来pipefd[0]
。您执行了一系列dup2
调用,导致 shell 尝试从管道的写入端读取 (STDIN)。
一旦你交换了枚举中的数字,你的 shell 就会永远停留在read
.这可能也不是您想要的,但是当您有一个通过管道连接到自身的 shell 时,这就是您所能期望的一切。
假设您尝试使用父进程中的 shell,则需要调用pipe
两次(并且都在父进程中):您写入的管道之一(shell 在 stdin 上读取)和 shell 写入的另一个管道到 (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
信号处理程序代码中的值)。