为什么 linux 不能写超过 2147479552 字节?
Why can't linux write more than 2147479552 bytes?
在 man 2 write
中,NOTES
部分包含以下注释:
On Linux, write() (and similar system calls) will transfer at most 0x7ffff000 (2,147,479,552) bytes, returning the number of bytes actually transferred. (This is true on both 32-bit and 64-bit systems.)
- 这是为什么?
DESCRIPTION
路径有如下语句:
According to POSIX.1, if count is greater than SSIZE_MAX, the result is implementation-defined
SSIZE_MAX
比 0x7ffff000
大很多。为什么会有这张便条?
更新: 谢谢解答!如果有人感兴趣(并且为了更好的搜索引擎优化来帮助开发人员),所有具有该限制的功能是:
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
为什么会在这里?
我认为这不一定有充分的理由 - 我认为这基本上是历史文物。让我用一些 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 <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 的写入是实现定义的行为,并且不大于该限制。不过,标准中还有另外两句我认为很重要:
The write() function shall attempt to write nbyte bytes from the buffer pointed to by buf to the file associated with the open file descriptor, fildes.
[...]
Upon successful completion, write() and pwrite() shall return the number of bytes actually written to the file associated with fildes. This number shall never be greater than nbyte. Otherwise, -1 shall be returned and errno set to indicate the error.
标准明确允许部分写入。因此,所有调用 write() needs to wrap calls to write() in a loop which retries short writes.
的代码
是否应该提高限额?
忽略历史包袱和标准,今天有理由提高这个限制吗?
我认为答案是否定的。 write() 缓冲区的最佳大小是在尝试避免内核和用户空间之间过多的上下文切换与确保您的数据尽可能适合缓存之间的权衡。
coreutils 程序(提供 cat、cp 等)使用 128KiB 的缓冲区大小。硬件的最佳尺寸可能稍大或稍小。但是 2GB 缓冲区不太可能会更快。
在 man 2 write
中,NOTES
部分包含以下注释:
On Linux, write() (and similar system calls) will transfer at most 0x7ffff000 (2,147,479,552) bytes, returning the number of bytes actually transferred. (This is true on both 32-bit and 64-bit systems.)
- 这是为什么?
DESCRIPTION
路径有如下语句:
According to POSIX.1, if count is greater than SSIZE_MAX, the result is implementation-defined
SSIZE_MAX
比 0x7ffff000
大很多。为什么会有这张便条?
更新: 谢谢解答!如果有人感兴趣(并且为了更好的搜索引擎优化来帮助开发人员),所有具有该限制的功能是:
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
为什么会在这里?
我认为这不一定有充分的理由 - 我认为这基本上是历史文物。让我用一些 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 <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 的写入是实现定义的行为,并且不大于该限制。不过,标准中还有另外两句我认为很重要:
The write() function shall attempt to write nbyte bytes from the buffer pointed to by buf to the file associated with the open file descriptor, fildes.
[...]
Upon successful completion, write() and pwrite() shall return the number of bytes actually written to the file associated with fildes. This number shall never be greater than nbyte. Otherwise, -1 shall be returned and errno set to indicate the error.
标准明确允许部分写入。因此,所有调用 write() needs to wrap calls to write() in a loop which retries short writes.
的代码是否应该提高限额?
忽略历史包袱和标准,今天有理由提高这个限制吗?
我认为答案是否定的。 write() 缓冲区的最佳大小是在尝试避免内核和用户空间之间过多的上下文切换与确保您的数据尽可能适合缓存之间的权衡。
coreutils 程序(提供 cat、cp 等)使用 128KiB 的缓冲区大小。硬件的最佳尺寸可能稍大或稍小。但是 2GB 缓冲区不太可能会更快。