【问题标题】:Why can't linux write more than 2147479552 bytes?为什么linux不能写超过2147479552字节?
【发布时间】:2023-04-03 08:38:02
【问题描述】:

man 2 write 中,NOTES 部分包含以下注释:

在 Linux 上,write()(和类似的系统调用)将最多传输 0x7ffff000 (2,147,479,552) 个字节,返回实际传输的字节数。 (在 32 位和 64 位系统上都是如此。)

  1. 这是为什么呢?
  2. DESCRIPTION路径有以下语句:

根据 POSIX.1,如果 count 大于 SSIZE_MAX,则结果为 implementation-defined

SSIZE_MAX0x7ffff000 大得多。为什么会有这个注释?

更新:感谢您的回答!如果有人有兴趣(并且为了更好的 SEO 来帮助开发人员),所有具有该限制的功能是:

  • read
  • write
  • sendfile

要找到这一点,只需全文搜索手册:

 % 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

【问题讨论】:

标签: c linux undefined-behavior


【解决方案1】:

为什么会在这里?

我认为这不一定有充分的理由 - 我认为这基本上是一个历史文物。让我用一些 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;

该常量定义为整数最大值和页面掩码的与。这大致等于最大整数大小减去一页的大小。

#define MAX_RW_COUNT (INT_MAX & PAGE_MASK)

这就是0x7ffff000 的来源 - 您的平台有 4096 字节宽的页面,即 212,因此它是未设置底部 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.

所以,这给了我们一个改变的理由:为了防止整数溢出,写入的大小被限制在接近最大整数的大小。大多数周围的逻辑似乎都已更改为使用 long 或 size_t,但检查仍然存在。

在此更改之前,为其提供大于 INT_MAX 的缓冲区将导致 EINVAL 错误:

if (unlikely(count > INT_MAX))
        goto Einval;

至于为什么设置这个限制,它存在于2.6.12之前,即第一个放入git的版本。我会让比我更有耐心的人解决这个问题。 :)

这符合 POSIX 吗?

戴上我的标准律师帽,我认为这实际上符合 POSIX。是的,POSIX 确实说大于 SSIZE_MAX 的写入是实现定义的行为,并且这不大于该限制。但是,标准中还有另外两句话我认为很重要:

write() 函数应尝试将 nbyte 字节从 buf 指向的缓冲区写入与打开的文件描述符 fildes 关联的文件。
[...]
成功完成后,write() 和 pwrite() 将返回实际写入与 fildes 关联的文件的字节数。此数字不得大于 nbyte。否则返回-1并设置errno表示错误。

标准明确允许部分写入。因此,所有调用 write() 的代码needs to wrap calls to write() in a loop which retries short writes.

是否应该提高限制?

忽略历史包袱和标准,今天有理由提高这个限制吗?

我认为答案是否定的。 write() 缓冲区的最佳大小是在尝试避免内核和用户空间之间过多的上下文切换和确保您的数据尽可能适合缓存之间的权衡。

coreutils 程序(提供 cat、cp 等)使用 128KiB 的缓冲区大小。您的硬件的最佳尺寸可能会稍大或稍小。但 2GB 缓冲区不太可能更快。

【讨论】:

    猜你喜欢
    • 2021-07-22
    • 2013-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-30
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多