猫 | dd 管道在没有 iflag=fullblock 的情况下导致部分读取,为什么被截断为 128KiB?

猫 | dd 管道在没有 iflag=fullblock 的情况下导致部分读取,为什么被截断为 128KiB?

考虑这个 1MiB 文件示例:

$ dd if=/dev/zero of=input.img bs=1M count=1
1+0 records in
1+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.00100595 s, 1.0 GB/s

如果我尝试在不使用的情况下cat将其通过管道传输,我无法读取每个块超过 128KiB 的数据:ddiflag=fullblock

$ cat input.img | dd of=output.img bs=128k count=1
1+0 records in
1+0 records out
131072 bytes (131 kB, 128 KiB) copied, 0.000221117 s, 593 MB/s
$ cat input.img | dd of=output.img bs=129k count=1
0+1 records in
0+1 records out
131072 bytes (131 kB, 128 KiB) copied, 0.000495317 s, 265 MB/s
$ cat input.img | dd of=output.img bs=1M count=1
0+1 records in
0+1 records out
131072 bytes (131 kB, 128 KiB) copied, 0.000437209 s, 300 MB/s

使用 2 个块计数,它甚至会打印一条警告:

$ cat input.img | dd of=output.img bs=129k count=2
dd: warning: partial read (131072 bytes); suggest iflag=fullblock
0+2 records in
0+2 records out
262144 bytes (262 kB, 256 KiB) copied, 0.00107657 s, 243 MB/s

我认为这可能是由于管道缓冲区大小造成的,但是我检查了它的大小如下https://www.golinuxhub.com/2018/05/how-to-view-and-increase-default-pipe-size-buffer.html它似乎设置为 65536 字节:

$ mkfifo /tmp/testfifo
$ python2
>>> fifo_fd = open('/tmp/testfifo', 'rb+')
>>> import fcntl
>>> fcntl.fcntl(fifo_fd, 1032)
65536

(注意我无法让它与 Python3 一起工作,我猜是因为https://bugs.python.org/issue20074

主要问题是(纯粹好奇):为什么读取会被截断为 128KiB?

(在 Arch Linux、内核 5.4.2、zsh 5.7.1 上测试。)

答案1

任何大于此值的块大小PIPE_BUF(现代 UNIX 上为 5k,Linux 上为 4k)都不允许在没有碎片的情况下通过管道。

这意味着,它可能被分割,但没有任何分割的资助。

是否能够读取更多内容PIPE_BUF取决于内核的内存状态,该状态控制内核何时停止写入端以防止其占用所有内核内存。它还取决于读取端的时序和调度,以及在积累了足够数量的数据后是否被唤醒。

顺便说一句:dd名为的选项iflag=fullblock是供应商特定的扩展。避免使用它,因为它是非标准的。

另请注意,gcat使用的写入块大小为 128k,而 UNIX 版本cat基于mmap()并使用写入大小为8Mbyte.因此,如果您确实在遗传 UNIX 上运行此测试,您可能能够读取大于 128k 的块,

相关内容