Lar*_*tin 5 c linux undefined-behavior
该man 2 write部分NOTES包含以下注释:
在 Linux 上,write()(和类似的系统调用)最多传输 0x7ffff000 (2,147,479,552) 个字节,返回实际传输的字节数。(这在 32 位和 64 位系统上都是如此。)
DESCRIPTION中有如下语句:根据 POSIX.1,如果 count 大于 SSIZE_MAX,则结果是实现定义的
SSIZE_MAX远大于0x7ffff000. 为什么会有这个注释?
更新:感谢您的回答!如果有人感兴趣(为了更好的 SEO 来帮助开发人员),所有具有该限制的功能是:
readwritesendfile要找到这一点,只需全文搜索手册即可:
% man -wK "0x7ffff000"
/usr/share/man/man2/write.2.gz
/usr/share/man/man2/read.2.gz
/usr/share/man/man2/sendfile.2.gz
/usr/share/man/man2/sendfile.2.gz
Run Code Online (Sandbox Code Playgroud)
我认为这不一定有充分的理由——我认为这基本上是一个历史文物。让我用一些 git 考古学来解释一下。
在当前的 Linux 中,此限制由以下因素控制MAX_RW_COUNT:
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
[...]
if (count > MAX_RW_COUNT)
count = MAX_RW_COUNT;
Run Code Online (Sandbox Code Playgroud)
该常量被定义为整数最大值和页掩码的AND。这大致等于最大整数大小减去一页的大小。
#define MAX_RW_COUNT (INT_MAX & PAGE_MASK)
Run Code Online (Sandbox Code Playgroud)
这就是0x7ffff000来自 - 你的平台有 4096 字节宽的页面,即 2 12,所以它是最大整数值,底部 12 位未设置。
最后一次更改此设置的提交(忽略只是移动内容的提交)是 e28cc71572da3。
Author: Linus Torvalds <torvalds@g5.osdl.org>
Date: Wed Jan 4 16:20:40 2006 -0800
Relax the rw_verify_area() error checking.
In particular, allow over-large read- or write-requests to be downgraded
to a more reasonable range, rather than considering them outright errors.
We want to protect lower layers from (the sadly all too common) overflow
conditions, but prefer to do so by chopping the requests up, rather than
just refusing them outright.
Run Code Online (Sandbox Code Playgroud)
因此,这给了我们进行更改的原因:为了防止整数溢出,写入的大小被限制在接近最大整数的大小。大多数周围的逻辑似乎已更改为使用 long 或 size_t,但检查仍然存在。
在此更改之前,给它一个大于 INT_MAX 的缓冲区将导致 EINVAL 错误:
if (unlikely(count > INT_MAX))
goto Einval;
Run Code Online (Sandbox Code Playgroud)
在此之前,每个文件都有一个由文件系统设置的最大 IO 大小,而不是硬编码限制。
if (unlikely(count > file->f_maxcount))
goto Einval;
Run Code Online (Sandbox Code Playgroud)
此电子邮件中描述了添加每个文件系统的限制。
然而,没有文件系统改变过 INT_MAX 的最大计数,因此这个功能在不到一年后被删除之前从未被使用过。我找不到任何关于为什么添加此功能的讨论。
戴上我的标准律师帽子,我认为这实际上符合 POSIX 标准。是的,POSIX 确实说大于 SSIZE_MAX 的写入是实现定义的行为,并且这不大于该限制。然而,标准中还有另外两句话我认为很重要:
write() 函数应尝试将 buf 指向的缓冲区中的 nbyte 字节写入与打开的文件描述符 fildes 关联的文件。
[...]
成功完成后,write() 和 pwrite() 应返回实际写入与 fildes 关联的文件的字节数。该数字绝不能大于 nbyte。否则,应返回-1并设置errno以指示错误。
标准明确允许部分写入。因此,所有调用 write() 的代码都需要将对 write() 的调用包装在重试短写入的循环中。
忽略历史包袱和标准,今天有理由提高这个限制吗?
我认为答案是否定的。write() 缓冲区的最佳大小是在尝试避免内核和用户空间之间过多的上下文切换与确保数据尽可能适合缓存之间的权衡。
coreutils 程序(提供 cat、cp 等)使用 128KiB 的缓冲区大小。您的硬件的最佳大小可能稍大或稍小。但 2GB 缓冲区不太可能更快。
| 归档时间: |
|
| 查看次数: |
959 次 |
| 最近记录: |