为什么linux不能写入超过2147479552字节?

问题描述 投票:0回答:1

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

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

  1. 这是为什么?
  2. DESCRIPTION
    路径有如下句子:

根据 POSIX.1,如果 count 大于 SSIZE_MAX,则结果是实现定义的

SSIZE_MAX
0x7ffff000
大得多。为什么会有这个注释?

更新:感谢您的回答!如果有人感兴趣(为了更好的 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个回答
6
投票

为什么会在这里?

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

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

#define MAX_RW_COUNT (INT_MAX & PAGE_MASK)

这就是

0x7ffff000
的来源 - 你的平台有 4096 字节宽的页面,即 212,所以它是未设置底部 12 位的最大整数值。

更改此设置的最后一次提交(忽略只是移动内容的提交)是 e28cc71572da3。

Author: Linus Torvalds <[email protected]>
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;

在此之前,每个文件都有一个由文件系统设置的最大 IO 大小,而不是硬编码限制。

    if (unlikely(count > file->f_maxcount))
         goto Einval;

添加每个文件系统的限制在此电子邮件中进行了描述。

但是,没有文件系统更改过 INT_MAX 的最大计数,因此该功能在不到一年后被删除之前从未被使用过。我找不到任何关于为什么添加此功能的讨论。 这符合 POSIX 标准吗?

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

write() 函数
应尝试

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

标准明确允许部分写入。因此,所有调用 write() 的代码都需要将对 write() 的调用包装在重试短写入的循环中。

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.