C 读取共享管道时被阻止的系统调用

C 读取共享管道时被阻止的系统调用,c,unix,pipe,fork,C,Unix,Pipe,Fork,我是Unix系统编程新手,我正在努力理解文件描述符和管道。让我们考虑一下这个简单的代码: #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include <string.h> int main() { int fd[2], p; char *m = "123456789\n", c; pipe(fd); p = fork(); if (p == 0)

我是Unix系统编程新手,我正在努力理解文件描述符和管道。让我们考虑一下这个简单的代码:

#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>

int main() {
  int fd[2], p;
  char *m = "123456789\n", c;
  pipe(fd);
  p = fork();
  if (p == 0) {
    // child
    while(read(fd[0], &c, 1) > 0) write(1, &c, 1);
  }
  else {
    // parent
    write(fd[1], m, strlen(m));
    close(fd[1]);
    wait(NULL);
  }
  exit (0);
}
#包括
#包括
#包括
#包括
int main(){
int-fd[2],p;
char*m=“123456789\n”,c;
管道(fd);
p=fork();
如果(p==0){
//孩子
而(读(fd[0],&c,1)>0)写(1,&c,1);
}
否则{
//母公司
写入(fd[1],m,strlen(m));
关闭(fd[1]);
等待(空);
}
出口(0);
}
当我编译并运行代码时,它会输出123456789,但除非我发出^C,否则进程永远不会结束。实际上,这两个进程在htop中都显示为已停止

如果孩子在read()之前关闭了fd[1],那么它似乎工作正常,但我不明白为什么。fd在两个进程之间共享,父进程在写入后关闭fd[1]。那么为什么孩子在阅读时得不到EOF呢

提前谢谢你

如果孩子在read()之前关闭了fd[1],那么它似乎工作正常,但我不明白为什么


这就是你需要做的。没有比这更多的了。从管道的读取端读取不会返回0(发出EOF信号),直到内核确定不会再向该管道的写入端写入任何内容,并且只要该管道在任何位置(包括执行读取的进程)仍处于打开状态,就无法确定这一点。

,首先,您的父进程正在等待子进程在
wait(2)
系统调用中终止,而您的子进程在管道中被阻塞,无法读取另一个字符的
read(2)
。两个进程都被阻止。。。因此,你需要从外部采取行动,将它们去除。问题是,子进程不关闭它的管道写入描述符(父进程也不关闭它的管道读取描述符,但这在这里不起作用),只是管道在至少一个这样的写入描述符仍然打开时阻塞了任何读卡器。只有当所有写入描述符都关闭时,读取才会向读取器返回
0

当您执行
fork(2)
时,两个管道描述符(
fd[0]
fd[1]
)都被
dup()
复制到子进程上,因此您有一个管道,其中有两个打开的文件描述符(一个在父进程中,一个在子进程中)用于写入,还有两个打开的描述符(同样,一个在父进程中,一个在子进程中)用于读取,因此,当一个writer保持管道打开以便写入(在本例中为子进程)时,子进程进行的读取仍然会阻塞。内核无法将此检测为异常,因为如果另一个线程(或信号处理程序)需要,子线程仍然可以在管道上写入

顺便说一句,我要评论一下你在代码中做得不好的地方:

  • 首先是从父级和子级中只考虑两种情况,从代码< > For()/代码>,但是如果叉失败,它将返回<代码> -1 < /Cord>,并且父进程将在没有读取进程的管道上写入,所以它可能会阻塞(如我所说,这不是你的情况,但它也是一个错误)您必须始终检查系统调用的错误,并且不要假设您的
    fork()
    调用永远不会失败(认为
    -1
    被视为
    !=0
    ,因此它属于父级代码)。只有一个系统调用可以在不检查错误的情况下执行,它是
    close(2)
    (尽管在这方面存在很多争议)
  • 这同样发生在
    read()
    write()
    上。更好的解决方案是使用更大的缓冲区(而不仅仅是一个字符,以减少程序进行的系统调用数量,从而加快速度),并使用
    read()
    的返回值作为
    write()
    调用的参数
您的程序(确实在我的系统上)只需插入以下行即可工作:

close(fd[1]);
在子代码中的
while
循环之前,如下所示:

#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>

int main() {
  int fd[2], p;
  char *m = "123456789\n", c;
  pipe(fd);
  p = fork();
  if (p == 0) {
    // child
    close(fd[1]);  // <--- this close is fundamental for the pipe to work properly.
    while(read(fd[0], &c, 1) > 0) write(1, &c, 1);
  }
  else if (p > 0) {
    // parent
    // another close(fd[0]); should be included here
    write(fd[1], m, strlen(m));
    close(fd[1]);
    wait(NULL);
  } else { 
    // include error processing for fork() here
  }
  exit (0);
}
#包括
#包括
#包括
#包括
int main(){
int-fd[2],p;
char*m=“123456789\n”,c;
管道(fd);
p=fork();
如果(p==0){
//孩子
关闭(fd[1]);//0)写入(1,&c,1);
}
否则,如果(p>0){
//母公司
//此处应包括另一个收盘价(fd[0])
写入(fd[1],m,strlen(m));
关闭(fd[1]);
等待(空);
}否则{
//此处包括fork()的错误处理
}
出口(0);
}

阅读我的答案。。。这里有解释。谢谢你详细的回答,现在已经非常清楚了!