给出以下代码:
int main(int argc, char *argv[]) { int pipefd[2]; pid_t cpid; char buf; if (argc != 2) { fprintf(stderr, "Usage: %s \n", argv[0]); exit(EXIT_FAILURE); } if (pipe(pipefd) == -1) { perror("pipe"); exit(EXIT_FAILURE); } cpid = fork(); if (cpid == -1) { perror("fork"); exit(EXIT_FAILURE); } if (cpid == 0) { /* Child reads from pipe */ close(pipefd[1]); /* Close unused write end */ while (read(pipefd[0], &buf, 1) > 0) write(STDOUT_FILENO, &buf, 1); write(STDOUT_FILENO, "\n", 1); close(pipefd[0]); _exit(EXIT_SUCCESS); } else { /* Parent writes argv[1] to pipe */ close(pipefd[0]); /* Close unused read end */ write(pipefd[1], argv[1], strlen(argv[1])); close(pipefd[1]); /* Reader will see EOF */ wait(NULL); /* Wait for child */ exit(EXIT_SUCCESS); } return 0; }
每当儿童进程想要从pipe道读取数据时,必须首先closurespipe道侧边的书写。 当我删除该行close(pipefd[1]);
从subprocess的if
,我基本上是说“好的,孩子可以从pipe道上读取,但是我允许父母同时写入pipe道”?
如果是这样的话,pipe道打开的时候会发生什么? 互不排斥?
每当儿童进程想要从管道读取数据时,必须首先关闭管道侧边的书写。
如果进程(父进程或子进程)不打算使用管道的写端,则应关闭该文件描述符。 类似于管道的读取结束。 系统将假设在任何进程打开写入结束时都可能发生写操作,即使唯一的进程是当前正试图从管道读取的进程,系统也不会报告EOF。 另外,如果你过度填充一个管道,并且仍然有一个读取结束的进程(即使这个进程是试图写入的进程),那么写入将会挂起,等待读者腾出空间来完成写入。
当我删除该行关闭(pipefd [1]); 从孩子的过程中,我基本上是说“好的,孩子可以从管道上读书,但是我允许父母同时写信给管道”?
没有; 你在说孩子可以写信给父母,也可以写信给父母。 任何具有管道的写入文件描述符的进程都可以写入管道。
如果是这样的话,管道是开放的读写都会发生什么 – 不互相排斥?
没有任何相互排斥。 任何打开管道写描述符的进程都可以随时写入管道。 内核确保两个并发写操作实际上是串行化的。 管道读取描述符打开的任何进程可以随时从管道中读取; 内核确保两个并发读操作获得不同的数据字节。
通过确保只有一个进程打开进行写入,并且只有一个进程打开进行读取,确保管道是单向使用的。 但是,这是一个编程决定。 你可以有N个进程,写入端是开放的,M个进程是读取端是开放的(并且可能会有N个和M个进程集之间共同的进程),他们都可以令人惊讶地工作。 但是,在写入数据之后,您无法预测数据包的读取位置。
fork()复制文件句柄,因此每个管道的末端都有两个句柄。
现在,考虑一下。 如果父母没有关闭未使用的管道末端,则仍然有两个手柄。 如果孩子死亡,孩子方面的手柄就会消失,但是父母仍然拥有敞开的手柄 – 因此,由于管子仍然是完全有效的,所以不会有“破损的管子”或“EOF”到达。 没有人把数据放进去了。
当然,对于另一个方向也一样。
是的,父母/孩子仍然可以使用手柄写入自己的管道; 我不记得这个用例,但它仍然给你同步的问题。
当管道被创建时,它的两端是读端和写端。 这些是用户文件描述符表中的条目。
类似地,在文件表中将有两个条目作为读取结束和写入结束的引用计数。
现在当你fork的时候,会创建一个子文件,即文件描述符被复制,因此文件表中两端的引用计数为2。
现在“当我删除该行关闭(pipefd [1])” – >在这种情况下,即使父母已完成写作,您的while循环下面这行会永远阻止读取返回0(即EOF)。 这种情况发生,因为即使父节点完成了写操作并关闭了管道的写入端,File表中的写入结束的引用计数仍然是1(最初是2),所以读取函数仍在等待某些数据到达这将永远不会发生。
现在如果你没有写“close(pipefd [0]);” 在父项中,这个当前的代码可能不会显示任何问题,因为你正在父项中写一次。
但是,如果你不止一次写入,那么理想情况下,你会想要得到一个错误(如果孩子不再读取),但由于父母的读取结束没有关闭,你将不会得到错误(即使小孩不在那里读)。
所以当我们不断地读写数据的时候,不关闭未使用的目标的问题就变得很明显了。 如果我们只是读取/写入数据,这可能并不明显。
就像如果不是在孩子的阅读循环,你只使用下面的一行,你一次得到的所有数据,而不是关心检查EOF,即使你没有写“关闭(pipefd [1]);” 在孩子。
read(pipefd[0], buf, sizeof(buf));//buf is a character array sufficiently large
SunOS的pipe()的手册页: – 仅在一端(所有写入文件描述符关闭)的空管道(无缓冲数据)上读取调用将返回一个EOF(文件结尾)。
A SIGPIPE signal is generated if a write on a pipe with only one end is attempted.