问题是您没有关闭所有未使用的管道文件描述符。例如,在exec("/bin/grep", argv) 的最后一个分支中,您正在关闭p1[1] 和dup2()ing p1[0],但您没有关闭p[0] 或p[1]。因此,当 ls 完成对 uniq 的写入后,该管道将保持打开状态,因为您仍然有对它的悬空引用。
您也没有检查任何系统调用是否有错误,这是应该的。
这是一个修订版(稍作修改,因为 grep 和 uniq 在我系统上的不同位置):
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int p1to2[2];
int p2to3[2];
if ( pipe(p1to2) == -1 || pipe(p2to3) == -1 ) {
perror("error calling pipe()");
return EXIT_FAILURE;
}
pid_t pid;
if ( (pid = fork()) == -1 ) {
perror("error calling first fork()");
return EXIT_FAILURE;
}
else if (pid == 0) {
if ( close(p1to2[0]) == -1 ) {
perror("error calling close() on p1to2[0]");
return EXIT_FAILURE;
}
if ( p1to2[1] != STDOUT_FILENO ) {
if ( dup2(p1to2[1], STDOUT_FILENO) == -1 ) {
perror("error calling dup2() on p1to2[1]");
return EXIT_FAILURE;
}
if ( close(p1to2[1]) == -1 ) {
perror("error calling close() on p1to2[1]");
return EXIT_FAILURE;
}
}
if ( close(p2to3[0]) == -1 || close(p2to3[1]) == -1 ) {
perror("error calling close() on p2to3");
return EXIT_FAILURE;
}
char *argv[] = {"ls", "-l", NULL};
if ( execv("/bin/ls", argv) == -1 ) {
perror("couldn't execute /bin/ls");
return EXIT_FAILURE;
}
} else {
if ( (pid = fork()) == -1 ) {
perror("error calling second fork()");
return EXIT_FAILURE;
}
else if ( pid == 0 ) {
if ( close(p1to2[1]) == -1 ) {
perror("error calling close() on p1to2[1]");
return EXIT_FAILURE;
}
if ( p1to2[0] != STDIN_FILENO ) {
if ( dup2(p1to2[0], STDIN_FILENO) == -1 ) {
perror("error calling dup2() on p1to2[0]");
return EXIT_FAILURE;
}
if ( close(p1to2[0]) == -1 ) {
perror("error calling close() on p1to2[0]");
return EXIT_FAILURE;
}
}
if ( close(p2to3[0]) == -1 ) {
perror("error calling close() on p2to3[0]");
return EXIT_FAILURE;
}
if ( p2to3[1] != STDOUT_FILENO ) {
if ( dup2(p2to3[1], STDOUT_FILENO) == -1 ) {
perror("error calling dup2() on p2to3[1]");
return EXIT_FAILURE;
}
if ( close(p2to3[1]) == -1 ) {
perror("error calling close() on p2to3[1]");
return EXIT_FAILURE;
}
}
char *argv[] = {"uniq", NULL};
if ( execv("/usr/bin/uniq", argv) == -1 ) {
perror("couldn't execute /usr/bin/uniq");
return EXIT_FAILURE;
}
} else {
if ( close(p1to2[0]) == -1 || close(p1to2[1]) == -1 ) {
perror("error calling close() on p1to2");
return EXIT_FAILURE;
}
if ( close(p2to3[1]) == -1 ){
perror("error calling close() on p2to3[1]");
return EXIT_FAILURE;
}
if ( p2to3[0] != STDIN_FILENO ) {
if ( dup2(p2to3[0], STDIN_FILENO) == -1 ) {
perror("error calling dup2() on p2to3[0]");
return EXIT_FAILURE;
}
if ( close(p2to3[0]) == -1 ) {
perror("error calling close() on p2to3[0]");
return EXIT_FAILURE;
}
}
char *argv[] = {"grep", "pipes", NULL};
if ( execv("/usr/bin/grep", argv) == -1 ) {
perror("couldn't execute /usr/bin/grep");
return EXIT_FAILURE;
}
}
}
}
和输出:
paul@horus:~/src/sandbox$ ./pipes
-rwxr-xr-x 1 paul staff 8812 Oct 25 12:21 pipes
-rw-r--r-- 1 paul staff 3817 Oct 25 12:21 pipes.c
-rw------- 1 paul staff 660 Oct 25 11:03 pipes.c.BAK
paul@horus:~/src/sandbox$
顺便说一句,当你有多个管道时,很容易混淆,close()s,dup2()s,以及像p和p1这样的变量,特别是当你添加错误检查时需要做。这是一个很好的例子,说明将程序组合成函数可以极大地帮助并避免由于难以弄清楚发生了什么而引入错误。
这是一个建议的组合,我建议main() 函数更容易理解、推理和排除故障,在这里:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
void make_pipes(int * p, ...);
pid_t fork_or_die(void);
void close_pipe_pair(int * p);
void make_std_reader(int * p);
void make_std_writer(int * p);
void execv_or_die(const char * path, char * const argv[]);
/* Main function */
int main(void)
{
pid_t pid;
int p1to2[2], p2to3[2];
make_pipes(p1to2, p2to3, NULL);
if ( (pid = fork_or_die()) == 0 ) {
make_std_writer(p1to2);
close_pipe_pair(p2to3);
char * args[] = {"ls", "-l", NULL};
execv_or_die("/bin/ls", args);
} else {
if ( (pid = fork_or_die()) == 0 ) {
make_std_reader(p1to2);
make_std_writer(p2to3);
char * args[] = {"uniq", NULL};
execv_or_die("/usr/bin/uniq", args);
} else {
close_pipe_pair(p1to2);
make_std_reader(p2to3);
char * args[] = {"grep", "pipes", NULL};
execv_or_die("/usr/bin/grep", args);
}
}
}
/* Creates a pipe for each array in the NULL terminated arg list */
void make_pipes(int * p, ...)
{
va_list ap;
va_start(ap, p);
while ( p ) {
if ( pipe(p) == -1 ) {
perror("error calling pipe()");
exit(EXIT_FAILURE);
}
p = va_arg(ap, int *);
}
va_end(ap);
}
/* Calls fork() and exits on error */
pid_t fork_or_die(void)
{
pid_t p = fork();
if ( p == -1 ) {
perror("error calling fork()");
exit(EXIT_FAILURE);
}
return p;
}
/* Closes a pipe pair and exits on error */
void close_pipe_pair(int * p)
{
if ( close(p[0]) == -1 || close(p[1]) == -1 ) {
perror("error calling close() in close_pipe_pair()");
exit(EXIT_FAILURE);
}
}
/* Closes the write end of a pipe and duplicates
* the read end into STDIN_FILENO, exiting on error */
void make_std_reader(int * p)
{
static const int read_end = 0;
static const int write_end = 1;
if ( close(p[write_end]) == -1 ) {
perror("error calling close() in make_std_reader()");
exit(EXIT_FAILURE);
}
if ( p[read_end] != STDIN_FILENO ) {
if ( dup2(p[read_end], STDIN_FILENO) == -1 ) {
perror("error calling dup2() in make_std_reader()");
exit(EXIT_FAILURE);
}
if ( close(p[read_end]) == -1 ) {
perror("error calling close() in make_std_reader()");
exit(EXIT_FAILURE);
}
}
}
/* Closes the read end of a pipe and duplicates
* the write end into STDOUT_FILENO, exiting on error */
void make_std_writer(int * p)
{
static const int read_end = 0;
static const int write_end = 1;
if ( close(p[read_end]) == -1 ) {
perror("error calling close() in make_std_writer()");
exit(EXIT_FAILURE);
}
if ( p[write_end] != STDOUT_FILENO ) {
if ( dup2(p[write_end], STDOUT_FILENO) == -1 ) {
perror("error calling dup2() in make_std_writer()");
exit(EXIT_FAILURE);
}
if ( close(p[write_end]) == -1 ) {
perror("error calling close() in make_std_writer()");
exit(EXIT_FAILURE);
}
}
}
/* Calls execv() and exits on error */
void execv_or_die(const char * path, char * const argv[])
{
if ( execv(path, argv) == -1 ) {
perror("error calling execv()");
exit(EXIT_FAILURE);
}
}