在下面的程序中(不是我的程序,而是我修改过的程序),子进程对管道进行两次写入。当我运行程序时,我得到以下输出:
Received string: Hello, world!
This is the child process.
父进程执行的读取如何从管道缓冲区中捕获这两个字符串?什么(如果有的话)阻止父进程在读取第一个字符串(或第一个字符串的第一个字符串)之后假设没有其他内容可以从缓冲区中读取并退出?
有问题的节目:
int main(void)
{
int fd[2], nbytes;
pid_t childpid;
char string[] = "Hello, world!\n";
char string2[] = "This is the child process.\n";
char readbuffer[80];
pipe(fd);
if((childpid = fork()) == -1)
{
perror("fork");
return 1;
}
if(childpid == 0)
{
/* Child process closes pipe's input file descriptor */
close(fd[0]);
/* Send "string" through the output side of pipe */
write(fd[1], string, (strlen(string)));
write(fd[1], string2, (strlen(string2)+1));
return 1;
}
else
{
/* Parent process closes pipe's output file descriptor */
close(fd[1]);
/* Read in a string from the pipe */
nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s", readbuffer);
}
return 0;
}
答案 0 :(得分:1)
此处的关键是read()
缓冲区大小80
。
通常read()
阻止正在调用它的进程(设置为处于休眠状态),直到某些条件不发生为止,例如:
EOF
)或发生错误^C
),这可能是您的情况,子进程退出,系统发送损坏的管道信号到父进程(进程唤醒和read()
获取整个缓冲区)请注意,这些条件取决于您正在阅读的子系统,例如pipe
。子系统可能具有不同的属性,如缓冲区大小。一个荒谬的例子:如果内核端上的管道缓冲区大小小于或等于第一次写入,读取进程将在之前唤醒,返回截断的缓冲区。
答案 1 :(得分:0)
父级将读取最多80个字节。因此,第一个write
系统调用(以及第二个)将把第一个字符串放在相对于管道的内核缓冲区中。 read
系统调用最多会读取80个字节,因此会向内核请求数据,内核将返回缓冲区中前80个字节的数据(因此不可能只读取字符串的第一个字符,单个写入整个字符串,单个阻塞读取80个字节)。
如你所说,孩子和父母之间没有同步。 事实上,在我的linux系统上,你的程序有时只会打印第一个字符串,有时会打印它们。
问题是经典的种族条件问题,有时候孩子只会写第一个字符串,然后调度父级,它会读取第一个字符串,然后程序将结束(或者控件将在read
之后返回给子节点,子节点会将第二个字符串写入管道缓冲区,但实际上没有人会读取数据。)
您应该在程序中放置一些同步功能,例如可以在close(fd[1]);
[...]
else
{
/* Parent process closes up output side of pipe */
close(fd[1]);
/* Synchronize parent and child */
wait(NULL);
/* Read in a string from the pipe */
nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s", readbuffer);
}
[...]
然后,只有当两个字符串正确放入缓冲区时,父级才会读取数据。
答案 2 :(得分:0)
这是如何写入管道的示例。
此示例用于父级写入子级,但逻辑非常相似,无论数据流方向如何。
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 to exit */
exit(EXIT_SUCCESS);
除此之外,这意味着当从管道中读取所有内容时,read()的调用者将获得返回值0。
这也意味着读者在循环中读取,直到返回错误条件指示或返回0。
答案 3 :(得分:0)
...当没有什么东西可以阅读时,读取功能如何“知道”?
成功read()
会返回基础对象中的可用内容,但不会超过请求。
成功完成后,当nbyte大于0时,read()将标记更新文件的st_atime字段,并返回读取的字节数。这个数字永远不会更大 比nbyte。如果文件中剩余的字节数小于nbyte,如果read()请求被信号中断,或者文件是管道或FIFO,则返回的值可能小于nbyte 或特殊文件,并且可以立即读取少于nbyte的字节。
答案 4 :(得分:0)
我喜欢这个问题。它让我记得过去的时光......
元答案:只知道流是否已关闭
答案:您的流程会读取它可以/必须读取的所有内容,而不会在'\ n'
处停止但我想,你想写/读记录。
尽管其他人我尝试回答而没有区分类型的流。我在这里做了一个免责声明:
这一切都取决于操作系统和流类型 - 以及两个(!)方面的选项
基本:
让我们说一个进程写一个其他的读取 - 很容易。
不,不是。
想象一下 - 总是;-) - 他们将字符(字节)放入流中,并且可以 - 慢慢地 - 快速 - 逐个或全部缓冲。
你读它 - 不是 - 不是你而不是你的程序 - 介于两者之间 - 逐字节或块或者它们之间的层如何。
所以如果你想要一个记录,那么只有三种可能性:
再次回答:
在你的情况下,孩子将所有字符放在父母读取它们的流中 - 为什么其中一个要休息?
解决方案:
根据操作系统和语言以及库,您有时可以告诉操作系统创建/接受结束字符(例如\ n) - 有时您的写入读取功能会为您执行此操作