提问人:John 提问时间:5/29/2022 最后编辑:John 更新时间:5/29/2022 访问量:133
“pipe”返回的两个文件描述符使用哪种类型的缓冲?
Which type of buffering is used by the two file descriptors returned by `pipe`?
问:
根据 的手册,其中说[强调我的]:man 2 pipe
pipe() 创建一个管道,一个可用于进程间通信的单向数据通道。数组 pipefd 用于返回两个文件描述符,这些描述符引用管道的末端。pipefd[0] 是指管道的读取端。pipefd[1] 是指管道的写入端。写入管道写入端的数据由内核缓冲,直到从管道的读取端读取数据。有关详细信息,请参见 pipe(7)
但是上面的引文并没有提到 返回的两个文件描述符使用了哪种类型的缓冲?man 7 pipe
pipe
根据文档,其中说有三种类型的缓冲[强调我的]:
标准 I/O 库缓冲 stdio 库缓冲数据,目的是最大限度地减少对 read() 和 write() 系统调用的调用次数。使用三种不同类型的缓冲:
完全(块)缓冲。当字符被写入流时,它们将被缓冲到缓冲区已满的点。在此阶段,数据将写入流引用的文件。同样,如果可能,读取将导致读取整个数据缓冲区。
线路缓冲。当字符被写入流时,它们将被缓冲,直到写入换行符。此时,包括换行符在内的数据行将写入流引用的文件。同样,对于读取,字符被读取到找到换行符的点。
无缓冲。当输出流未缓冲时,写入该流的任何数据都会立即写入与该流关联的文件。
ANSI C 标准规定标准输入和输出应完全缓冲,而标准误差应无缓冲。通常,标准输入和输出的设置是为终端设备提供线路缓冲,否则是完全缓冲的。
我做了一个简单的测试,这是代码片段:Ubuntu16.04
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <string>
#include <thread>
#include <array>
#include <iostream>
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
std::array<char, 1024> buf;
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 */
int size;
while ((size = read(pipefd[0], buf.data(), buf.size())) > 0)
std::cout << size << std::endl;
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 */
std::string str{"hello world"};
for(int i=0; i<3; i++)
{
write(pipefd[1], str.c_str(), str.size());
std::this_thread::sleep_for(std::chrono::seconds(3));
}
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
以下是上述代码片段的输出:
11
//about three seconds later
11
//about three seconds later
您会看到写入端写入管道的内容不包含 ,而读取端可以每三秒读出一个完整的字符串。所以我认为返回的两个文件描述符既不是也不是。去掉两个选择,那么只有.\n
pipe
block buffered
line buffered
unbuffered buffering
但手册也说:man 7 pipe
PIPE_BUF POSIX.1-2001 指出,小于 PIPE_BUF 字节的 write(2) 必须是原子的:输出数据作为连续序列写入管道。写更多 超过 PIPE_BUF 字节可能是非原子的:内核可能会将数据与其他进程写入的数据交错。POSIX.1-2001 要求PIPE_BUF至少 512 字节。(在 Linux 上,PIPE_BUF 为 4096 字节。
根据上面的引文,确实有两个文件描述符的缓冲区。
所以我真的很困惑 返回的两个文件描述符使用哪种类型的缓冲。由于每个文件描述符都提供了缓冲区,我如何按时收到字符串(即每三秒一个字符串,而不是三个字符串在一起)?pipe
谁能对这件事有所了解?
答:
在两个不同的地方有缓冲,重要的是不要混淆它们。
您引用的文档是关于“标准 I/O 库缓冲”的,它指的是标准 C 库 (libc)。它适用于 libc 函数,如 和 。这种缓冲发生在用户空间中,即在程序过程中。在后台,当刷新此缓冲区时,libc 会调用以将数据发送到基础文件描述符。fprintf
fwrite
write
但是,是对内核的直接¹系统调用,这与 libc 中的缓冲无关。您可以分辨出其中的区别,因为它适用于文件描述符,而不是 s。在代码中使用的 and 函数也是系统调用。pipe
FILE*
read
write
管道仍然在内核空间中缓冲,但在那里谈论“行缓冲”或“块缓冲”是没有意义的,因为没有进行自动刷新。如果缓冲区已满,则任何调用都会阻塞,直到再次有空间。内核中缓冲区耗尽的唯一方法是通过调用。write
read
¹ 通过一个薄包装器,它也在 C 库中,但这不是重点。
评论