int main()
{
int p[2];
int p1[2];
pipe(p);
pipe(p1);
int pid,status;
char buff[10000];
pid = fork();
if(pid == 0)
{
close(p[0]);
dup2(p[1],1);
close(p[1]);
char *argv[] = {"ls","-l",NULL};
execv("/bin/ls",argv);
}
else
{
wait(&status);
pid =fork();
if (pid ==0)
{
close(p[1]);
dup2(p[0],0);
close(p[0]);
close(p1[0]);
dup2(p1[1],1);
close(p1[1]);
char *argv[] = {"uniq",NULL};
execv("/bin/uniq",argv);
}
else
{
wait(&status);
close(p1[1]);
dup2(p1[0],0);
close(p1[0]);
char *argv[] = {"grep","^d",NULL};
execv("/bin/grep",argv);
}
}
}
为什么即使我已经正确关闭了末端(假设我已经关闭),读取也会在子进程(uniq 进程)中阻塞。尝试了 strace 并持续点击它 3 到 4 小时。我仍然想知道为什么它会阻塞......任何帮助都会有所帮助:)
最佳答案
问题是您没有关闭所有未使用的管道文件描述符。例如,在您的最后一个分支中 exec("/bin/grep", argv)
,你正在关闭 p1[1]
和 dup2()
荷兰国际集团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()
小号,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);
}
}
关于c - 读取阻塞甚至关闭写入结束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26563553/