下面的程序准备使用
fork()
/exec()
/poll()
组合从子进程中捕获 stdout 和 stderr。它大部分工作正常,但在这里失败了:
if (close(pfds[j].fd) == -1)
perror("close");
如果子进程打印了很多输出。错误是:“关闭:错误的文件描述符”。而且,这个错误不会每次都出现。假设我运行该程序 10 次,它可能只显示 5 次“关闭:错误的文件描述符”。这让我一直在思考——我是否应该将这个
close()
保留在 poll()
循环中?
(注意:原程序可以在这里找到。为了简单起见,所有不相关的错误处理都被删除了。)
int exec(char* argv1) {
int pipefd_out[2], pipefd_err[2];
pipe(pipefd_out);
pipe(pipefd_err);
pid_t child_pid = fork();
if (child_pid == 0) {
close(pipefd_out[0]);
close(pipefd_err[0]);
dup2(pipefd_out[1], STDOUT_FILENO);
dup2(pipefd_err[1], STDERR_FILENO);
// Prepared a few possible cases, to demo different behaviors
if (atoi(argv1) == 0) {
execl("./sub.out", "./sub.out", NULL);
} else if (atoi(argv1) == 1) {
const char *const args[] = {"./sub.out", "segfault", NULL};
execv(args[0], args);
} else if (atoi(argv1) == 2) {
const char *const args[] = {"./sub.out", "flooding", NULL};
execv(args[0], args);
} else if (atoi(argv1) == 3) {
const char *const args[] = {"/bin/ls", "-l", "/tmp/", NULL};
execv(args[0], args);
} else {
const char *const args[] = {"/bin/ls", "-l",
"/path/that/definitely/does/not/exist/", NULL};
execv(args[0], args);
}
perror("execl()/execv()");
_exit(EXIT_FAILURE);
}
close(pipefd_out[1]);
close(pipefd_err[1]);
struct pollfd pfds[] = {
{ pipefd_out[0], POLLIN, 0 },
{ pipefd_err[0], POLLIN, 0 },
};
int nfds = sizeof(pfds) / sizeof(struct pollfd);
int num_open_fds = nfds;
while (num_open_fds > 0) {
int ready = poll(pfds, nfds, -1);
if (ready == -1)
perror("poll()");
for (int j = 0; j < nfds; j++) {
if (pfds[j].revents != 0) {
char buf[4096] = {0};
if (pfds[j].revents & POLLIN) {
ssize_t s = read(pfds[j].fd, buf, sizeof(buf)-1);
if (s == -1)
perror("read()");
if (j == 0) { printf("<stdout>%s</stdout>\n", buf); }
else { printf("<stderr>%s</stderr>\n", buf); }
fflush(stdout);
} else { /* POLLERR | POLLHUP */
if (close(pfds[j].fd) == -1)
perror("close");
num_open_fds--;
}
}
}
}
int status;
waitpid(child_pid, &status, 0);
if (WIFEXITED(status)) {
printf("Child process exited normally, rc: %d\n", WEXITSTATUS(status));
} else {
printf("Child process exited unexpectedly\n");
}
return EXIT_SUCCESS;
}
int main(int argc, char** argv) {
if (argc != 2) {
printf("Usage: %s <0|1|2|3|4>\n", argv[0]);
return EXIT_FAILURE;
}
return exec(argv[1]);
}
描述符
pfds[j].fd
是您的管道之一。如果你得到一个错误或者它被关闭那么你需要关闭它,但是你也不应该在下一次迭代中继续将它传递给poll
。
并考虑一下
pfds[j].fd == pipefd_out[0]
发生的情况。您需要重新排列阵列以移除封闭的管道。或者在 poll
. 之前的循环内重新创建数组
也许是这样的:
if (close(pfds[j].fd) == -1)
perror("close");
num_open_fds--;
if (pfds[j] == pipefd_out[0])
pipefd_out[0] = -1; // This descriptor is now closed
if (pfds[j] == pipefd_err[0])
pipefd_err[0] = -1; // This descriptor is now closed
在
poll
之前创建数组:
size_t pfd_index = 0;
struct pollfd pfds[num_open_fds];
if (pipefd_out[0] != -1)
pfds[pfd_index++] = (struct pollfd){ pipefd_out[0], POLLIN, 0 };
if (pipefd_err[0] != -1)
pfds[pfd_index] = (struct pollfd){ pipefd_err[0], POLLIN, 0 };
int ready = poll(pfds, num_open_fds, -1);
// Etc...