这需要一点重构...
如果有 N 个流水线阶段,我们需要 N-1 个单独的 pipe 调用。代码只有一个。
每个流水线阶段都可以有自己的/私有的stderr 转移(例如):
cmd0 | cmd1 2>cmd1_stderr | cmd2 2>cmd2_stderr | cmd3
但是,代码假设 all stderr 将是相同的。
而且,它假设它必须打开stderr [at all]。如果我们有类似上面的内容,孩子应该只打开stderr。否则,每个孩子都应该继承父母的stderr [并且做什么]。
parent 进程正在做只有 child 应该做的事情(例如):更改 stdin/stdout/stderr 并执行命令。它应该主要是为了方便管道。
父级在创建/分叉循环中执行wait [针对每个子级]。这会导致父级在每一步都阻塞。
父母应该只在 second 循环中执行wait 并立即等待所有孩子。
因为您对cmd 的struct 定义没有发布,所以我不得不猜测一下意图。但是,我认为你应该有一个结构的 array,每个命令一个,而不是将 all 命令的 all 参数放在一个结构。
我创建了两个版本的代码。一个带有错误注释。第二个被清理和重组。两者都未经测试,但应该会给你一些想法。
这是带注释的版本:
// NOTE/BUG: each command in the pipeline can have its own/private
// stderr diversion
#if 0
struct cmd {
int pipes;
char *filev[3];
char **argv[MAXCMD][MAXARG];
};
#else
struct cmd {
char *stderr;
char *argv[100];
};
#endif
// NOTE/BUG: we need separate filev [and argv] for each pipeline stage
// so we need an _array_ of structs
int cmdcnt;
struct cmd cmdlist[30];
void
pipeline(void)
{
int pfd[2];
// NOTE/BUG: this only creates a single pipe -- we need N-1 pipes
#if 0
if (pipe(pfd) < 0)
exit(-1);
#endif
// NOTE/BUG: if child does _not_ have a private stderr it should just
// use the parent's stderr _unchanged_
for (int i = 0; i < cmdcnt; i++) {
pid_t pid;
// NOTE/BUG: here is the correct place to create the pipe
pid = fork();
int fd;
if (pid < 0)
exit(-1);
char **argv = cmd->argv;
char **filev = cmd->filev;
// child process
if (pid == 0) {
close(pfd[0]);
dup2(pfd[1], STDOUT_FILENO);
close(pfd[1]);
// NOTE/BUG: this does _not_ connect the input of cmd[N] to cmd[N-1]
#if 0
if (filev[0] != NULL && i == 0) {
fd = open(filev[0], O_RDONLY, 0);
dup2(fd, STDIN_FILENO);
close(fd);
}
#endif
if (filev[2] != NULL) {
fd = creat(filev[2], 0644);
dup2(fd, STDERR_FILENO);
close(fd);
}
if (execvp(cmd->argv[i][0], cmd->argv[i]) < 0)
levenshtein(cmd->argv[i][0], commands);
}
// parent process
if (pid > 0) {
// NOTE/BUG: parent should _not_ wait in the middle of the creation
// loop
if (cmd->bg > 0)
wait(NULL);
// NOTE/BUG: _parent_ should _not_ change its stdin/stderr/stdout
close(pfd[1]);
dup2(pfd[0], STDIN_FILENO);
close(pfd[0]);
if (filev[1] != NULL && i == (cmd->pipes - 1)) {
fd = creat(filev[1], 0644);
dup2(fd, STDOUT_FILENO);
close(fd);
}
if (filev[2] != NULL) {
fd = creat(filev[2], 0644);
dup2(fd, STDERR_FILENO);
close(fd);
}
// NOTE/BUG: _parent_ should _not_ execute the command
if (execvp(cmd->argv[i][0], cmd->argv[i]) < 0)
levenshtein(cmd->argv[i][0], commands);
}
}
}
这是重构后的版本:
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
struct cmd {
char *stderr;
char *argv[100];
};
char *filev[3];
#define CLOSEME(_fd) \
do { \
if (_fd < 0) \
break; \
close(_fd); \
_fd = -1; \
} while (0)
int run_in_background;
int cmdcnt;
struct cmd cmdlist[30];
int
pipeline(void)
{
int pfd[2] = { -1, -1 };
struct cmd *cmd;
char *file;
int oldfd;
pid_t pid;
pid_t lastpid = -1;
// open the common stderr
int errfd = -1;
if (filev[2] != NULL)
errfd = open(filev[2],O_APPEND | O_CREAT);
// start all commands
for (int i = 0; i < cmdcnt; i++) {
int iam_first = (i == 0);
int iam_last = (i == (cmdcnt - 1));
cmd = &cmdlist[i];
// get previous stage pipe descriptor
oldfd = pfd[0];
// create the pipe to the next stage
if (! iam_last) {
if (pipe(pfd) < 0)
exit(1);
}
pid = fork();
lastpid = pid;
int fd;
if (pid < 0)
exit(-1);
char **argv = cmd->argv;
// parent process
if (pid > 0) {
CLOSEME(pfd[1]);
continue;
}
// child process ...
// open stdin for _first_ command
fd = -1;
if (iam_first) {
file = filev[0];
if (file != NULL) {
fd = open(file, O_RDONLY, 0);
}
}
// connect stdin to previous stage pipe
else {
fd = oldfd;
oldfd = -1;
}
// connect stdin to correct source
if (fd >= 0) {
dup2(fd, STDIN_FILENO);
close(fd);
}
CLOSEME(oldfd);
// connect to stderr
file = cmd->stderr;
if (file != NULL) {
fd = creat(file, 0644);
dup2(fd, STDERR_FILENO);
close(fd);
}
else {
if (errfd >= 0)
dup2(errfd, STDERR_FILENO);
}
CLOSEME(errfd);
// connect stdout
// NOTE: does _not_ handle ">> outf" [only does "> outf"]
fd = -1;
if (iam_last) {
file = filev[1];
if (file != NULL) {
fd = open(file, O_WRONLY | O_CREAT, 0644);
dup2(fd, STDOUT_FILENO);
close(fd);
}
}
// execute the command
execvp(argv[0], argv);
exit(9);
}
CLOSEME(errfd);
int status;
int last_status = 0;
// parent waits for all pipeline stages to complete
if (! run_in_background) {
while (1) {
pid = wait(&status);
if (pid <= 0)
break;
if (pid == lastpid) {
last_status = status;
break;
}
}
}
return last_status;
}