你没有足够的电话来close()
!父级必须关闭其所有的管道副本。在将相关的管道描述符复制到标准输入和输出之后,孩子们也必须关闭每个管道描述符。否则,进程永远不会得到 EOF,因为有一个进程可以写入管道。
此代码使用我的stderr.h
和stderr.c
代码代替您的error.h
(如果您需要代码,请与我联系 - 请参阅我的个人资料)。它写出了safe_*
函数——或者,至少,我对它们的实现。
该dump_fds()
函数报告在 0-19 范围内打开了哪些文件描述符,这对于这个程序和大多数程序来说已经足够了;一个更复杂的版本sysconf()
用于确定要检查的文件描述符的数量,但该数量通常比使用中的数量大得多(例如 256 或更大)。我用它作为一种简单的方法来检查所有应该关闭的文件描述符是否已关闭。
有相当多的调试输出。调用execv()
提供了正确的参数列表。
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
//#include "error.h"
//#include "safe_functions.h"
#include "stderr.h"
extern pid_t safe_fork(void);
extern void safe_dup2(int old_fd, int new_fd);
extern void safe_execv(const char *prog, char **argv);
extern void safe_pipe(int *pipe_fds);
extern void *safe_malloc(size_t size);
extern int safe_wait(int *status);
extern void safe_close(int fd);
/* Report on open file descriptors (0..19) in process */
static void dump_fds(void)
{
struct stat b;
char buffer[32];
sprintf(buffer, "%d: ", getpid());
char *str = buffer + strlen(buffer);
for (int i = 0; i < 20; i++)
*str++ = (fstat(i, &b) == 0) ? 'o' : '-';
*str++ = '\n';
*str = '\0';
fputs(buffer, stderr);
}
static void close_pipes(int **descr, int argc)
{
for (int i = 0; i < argc; i++)
{
for (int j = 0; j < 2; j++)
{
if (descr[i][j] > 1)
{
err_remark("close %d\n", descr[i][j]);
safe_close(descr[i][j]);
}
}
}
}
static void call(char *filename, int in_descr, int out_descr, pid_t *sons, int n, int **descr, int argc)
{
sons[n] = safe_fork();
if (sons[n] == 0)
{
err_remark("call: %s\n", filename);
char *argv[2] = { filename, NULL };
err_remark("dup2(%d, %d)\n", in_descr, STDIN_FILENO);
err_remark("dup2(%d, %d)\n", out_descr, STDOUT_FILENO);
safe_dup2(in_descr, STDIN_FILENO);
safe_dup2(out_descr, STDOUT_FILENO);
close_pipes(descr, argc);
dump_fds();
safe_execv(argv[0], argv);
}
}
static int find_num(pid_t *sons, int n, pid_t id)
{
for (int i=0; i<n; i++)
{
if (sons[i] == id)
return i;
}
return -1;
}
int main(int argc, char **argv)
{
err_setarg0(argv[0]);
err_setlogopts(ERR_PID);
dump_fds();
int **descr;
descr = (int**)safe_malloc(argc*sizeof(int*));
for (int i=0; i<argc; i++)
descr[i] = (int*) safe_malloc(2*sizeof(int));
for (int i=1; i+1<argc; i++)
safe_pipe(descr[i]);
descr[0][0] = 0;
descr[argc-1][1] = 1;
pid_t *sons = safe_malloc((argc-1) * sizeof(pid_t));
for (int i=1; i<argc; i++)
{
err_remark("Command: %s\n", argv[i]);
call(argv[i], descr[i-1][0], descr[i][1], sons, i-1, descr, argc);
}
close_pipes(descr, argc);
while (1)
{
int status;
pid_t id = safe_wait(&status);
err_remark("wait: pid %d, status 0x%.4X\n", (int)id, status);
if (id == -1)
break;
if (WIFEXITED(status))
{
int num = find_num(sons, argc-1, id);
//safe_close(descr[num][0]);
//safe_close(descr[num+1][1]);
continue;
}
if (WIFSIGNALED(status))
{
int num = find_num(sons, argc-1, id);
err_remark("Process %s was terminated by signal %d", argv[num+1], WEXITSTATUS(status));
}
}
free(sons);
for (int i=0; i<argc; i++)
free(descr[i]);
free(descr);
return(0);
}
extern pid_t safe_fork(void)
{
pid_t pid = fork();
if (pid < 0)
err_syserr("Failed to fork() ");
return pid;
}
extern void safe_dup2(int old_fd, int new_fd)
{
if (dup2(old_fd, new_fd) < 0)
err_syserr("Failed to dup2(%d, %d) ", old_fd, new_fd);
}
extern void safe_execv(const char *prog, char **argv)
{
execv(prog, argv);
err_syserr("Failed to execv(\"%s\") ", prog);
}
extern void safe_pipe(int *pipe_fds)
{
assert(pipe_fds != 0);
if (pipe(pipe_fds) != 0)
err_syserr("Failed to pipe() ");
err_remark("pipe: %d, %d\n", pipe_fds[0], pipe_fds[1]);
}
extern void *safe_malloc(size_t size)
{
void *vp = malloc(size);
if (vp == 0)
err_syserr("Out of memory ");
return vp;
}
extern int safe_wait(int *status)
{
assert(status != 0);
return wait(status);
}
extern void safe_close(int fd)
{
if (close(fd) < 0)
err_syserr("Failed to close(%d)\n", fd);
}
示例输出
$ ./pipes-15845060 /bin/ps /usr/bin/sort /bin/cat
12096: ooo-----------------
pipes-15845060: pid=12096: pipe: 3, 4
pipes-15845060: pid=12096: pipe: 5, 6
pipes-15845060: pid=12096: Command: /bin/ps
pipes-15845060: pid=12096: Command: /usr/bin/sort
pipes-15845060: pid=12096: Command: /bin/cat
pipes-15845060: pid=12096: close 3
pipes-15845060: pipes-15845060: pid=12098: pid=12096: close 4
pipes-15845060: pid=12096: close 5
pipes-15845060: pid=12096: close 6
call: /bin/ps
pipes-15845060: pid=12098: dup2(0, 0)
pipes-15845060: pid=12098: dup2(4, 1)
pipes-15845060: pid=12099: call: /usr/bin/sort
pipes-15845060: pid=12099: dup2(3, 0)
pipes-15845060: pid=12099: dup2(6, 1)
pipes-15845060: pid=12098: pipes-15845060: pid=12099: close 3
pipes-15845060: pid=12099: close 4
pipes-15845060: pid=12099: close 5
pipes-15845060: pid=12099: close 6
12099: ooo-----------------
close 3
pipes-15845060: pid=12098: close 4
pipes-15845060: pid=12098: close 5
pipes-15845060: pid=12098: close 6
12098: ooo-----------------
pipes-15845060: pid=12100: call: /bin/cat
pipes-15845060: pid=12100: dup2(5, 0)
pipes-15845060: pid=12100: dup2(1, 1)
pipes-15845060: pid=12100: close 3
pipes-15845060: pid=12100: close 4
pipes-15845060: pid=12100: close 5
pipes-15845060: pid=12100: close 6
12100: ooo-----------------
pipes-15845060: pid=12096: wait: pid 12098, status 0x0000
563 ttys000 0:00.03 -sh
568 ttys001 0:00.03 -sh
578 ttys003 0:00.03 -sh
587 ttys002 0:00.03 -sh
588 ttys005 0:00.15 -sh
589 ttys004 0:00.20 -sh
PID TTY TIME CMD
12096 ttys004 0:00.00 ./pipes-15845060 /bin/ps /usr/bin/sort /bin/cat
12097 ttys004 0:00.00 sed /./s/^/ /
12099 ttys004 0:00.00 /usr/bin/sort
12100 ttys004 0:00.00 /bin/cat
pipes-15845060: pid=12096: wait: pid 12100, status 0x0000
pipes-15845060: pid=12096: wait: pid 12099, status 0x0000
pipes-15845060: pid=12096: wait: pid -1, status 0x0000
$