C 如何一次写入超过2G的()数据

C 如何一次写入超过2G的()数据,c,linux,mmap,C,Linux,Mmap,我已经定义了\u LARGEFILE64\u SOURCE和\u FILE\u OFFSET\u BITS 64 支持大于2G的open()文件。看来没问题 但是,如果我尝试一次写入超过2G的()数据(例如64G),write()将返回一个远小于64G的值(正好是2147479552)。我猜write()一次只能写入小于2G的数据 这是我的密码: #define _GNU_SOURCE #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #

我已经定义了
\u LARGEFILE64\u SOURCE
\u FILE\u OFFSET\u BITS 64
支持大于2G的open()文件。看来没问题

但是,如果我尝试一次写入超过2G的()数据(例如64G),write()将返回一个远小于64G的值(正好是2147479552)。我猜write()一次只能写入小于2G的数据

这是我的密码:

#define _GNU_SOURCE
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#define _FILE_OFFSET_BITS 64

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <mmap.h>

#define SPACE_SIZE 68719476736

int main()
{
    ssize_t err;
    void* zeros;
    int fd = open64("./test", O_CREAT|O_RDWR|O_LARGEFILE, 0644);
    assert(fd != -1);
    int zero_fd = open("/dev/zero", O_RDWR);
    assert(zero_fd != -1);

    zeros = mmap(NULL, SPACE_SIZE, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, zero_fd, 0);
    assert(zeros != (void *)-1);
    err = write(fd, zeros, SPACE_SIZE);
    assert(err == SPACE_SIZE);   // Received SIGABRT, err = 2147479552
    munmap(zeros, SPACE_SIZE);
}

事实上,这一事实在《人类书写》中有很好的记载:

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

因此,如果您使用的是Linux,则无法在对
write()
的单个调用中写入64GB


但是,通过寻找偏移量64GB-1并写入单个NUL字节,可以创建一个似乎包含64GB NUL字节的文件。(或者,正如@o11c在一篇评论中指出的那样,您可以使用
ftruncate(fd,SPACE_SIZE);
)这两种方法中的任何一种都会创建一个稀疏文件,它实际上不会占用太多磁盘空间,但它会像64GB的NUL一样工作。

您真的有(略多于)68719476736字节的RAM?2147479552实际上是2*1024*1024*1024-4096:正好比2GB少一页。(千兆字节是1024*1024*1024字节,而不是1000*1000*1000字节。不要听硬盘制造商的谎言。)您使用的是32位还是64位环境?我还想知道您实际拥有多少RAM。@zwol我想带有选项PRIVATE&ANONYMOUS的mmap可以处理超过物理内存限制的虚拟RAM区域。是吗?这有什么关系
write()
不能保证在任何情况下传输调用中请求的全部字节数,因此安全地
write()
多个字节的唯一方法是在循环中执行
write()
s,使用返回值跟踪您的位置和剩余要传输的字节数。正确设置后,您应该通过调用多少次来写入所有数据。
manwrite
在我的Linux系统上以以下相关语句结尾:“在Linux上,write()(和类似的系统调用)最多传输0x7ffff000(2147479552)字节,返回实际传输的字节数。”在代码中我找到了一个常量,它被定义为
INT\u MAX&PAGE\u MASK
。因此,在页面大小不同的平台上可能会有一些不同。(我认为所有Linux平台都使用32位
int
,所以这一部分不会改变。)有一种说法似乎是为了避免在可能的32位
ssize\t
返回值中出现有符号溢出。@zwol:我模糊地记得那条评论,但要点是:有一个限制;记录在案;RTFM将为每个人节省大量时间。我不知道阅读手册什么时候过时了,但我知道他们是如何继续成为获取信息的第一站的。难道不是
ftruncate
做的事情和在搜索后写一个字节一样,但是如果没有这种奇怪的“一点一点”的抖动?@rici我不得不说,我永远不会想到要看这个手册页——因为我想我已经知道了
手册页可以告诉我的一切。@rici:也许是Linux手册页项目中最新的手册页的制作和链接?另外,
ftruncate()
优于在末尾写入一个零,因为两者都创建稀疏文件,后者会导致至少一个块(全部零)写入磁盘。接下来,我们可以为文件数据实际保留存储空间。
ELF Header:
Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
Class:                             ELF64
Data:                              2's complement, little endian
Version:                           1 (current)
OS/ABI:                            UNIX - System V
ABI Version:                       0
Type:                              DYN (Shared object file)
Machine:                           Advanced Micro Devices X86-64
Version:                           0x1
Entry point address:               0x660
Start of program headers:          64 (bytes into file)
Start of section headers:          6672 (bytes into file)
Flags:                             0x0
Size of this header:               64 (bytes)
Size of program headers:           56 (bytes)
Number of program headers:         9
Size of section headers:           64 (bytes)
Number of section headers:         29
Section header string table index: 28