不能创建与MMAP /的malloc /开放等64位Linux系统大于2GB文件(can't

2019-10-19 20:31发布

好吧,我知道类似这样的问题已经以各种形式被提出之前,我已阅读所有这些,尝试,已提出的一切,但我仍然无法创建文件使用malloc,开放,lseek的,等等大于2GB的64位系统上嗒嗒阳光下的每招。

显然,我在这里写C。 我运行Fedora 20,我其实想的mmap文件,但不是地方失败了,我原来的方法是使用的open(),然后向lseek的位置所在的文件应该结束在这种情况下是在3GB,编辑:然后写在文件末尾位置实际创建该大小的文件字节,然后mmap的文件。 我不能lseek的过去2GB。 我不能malloc的超过2GB两种。 的ulimit -a等无不显示出无限,在/etc/security/limits.conf说明不了什么,....

当我尝试过去lseek的2GB我得到EINVAL的错误号和lseek的的RET VAL是-1.edit:尺寸参数lseek的是它被定义为一个长整型(64位有符号)off_t类型的,不是size_t型像我说的先前。

编辑:我已经尝试过定义_LARGEFILE64_SOURCE&_FILE_OFFSET_BITS 64并没有什么区别。 我还专门编制了64位,即-m64

我迷路了。 我不知道为什么我不能做到这一点。

任何帮助将不胜感激。

谢谢。

编辑:我已经删除了很多不正确完全的咿咿呀呀我的一部分,并已被后来处理了一些其他不重要的随笔。

我的2GB的问题是在多种不同类型的可怕马虎互换。 混合符号和无符号是问题。 基本上3GB位置我被传递给lseek的正在解释/化作-1GB的那样的位置和清楚lseek的didnt。 所以,我的坏。 那么傻。

我要改变使用posix_fallocate()作为P_L建议。 虽然它删除一个函数调用,即只需要而不是posix_fallocate一个lseek的,然后写,对我来说是不显著,这是一个事实,即posix_fallocate是做什么我想直接将lseek的方法没有。 因此,尤其应归功于P_L的提示,并特别感谢NominalAnimal其持久性,他知道更好间接地使我认识到,我不能指望这反过来又导致我接受posix_fallocate会工作,所以改为使用它。

不管端方法的予使用。 2GB的问题是完全属于我废话编码和再次感谢EOF,chux,P_L和乔纳森·莱弗勒谁贡献的所有信息,并导致我我为自己创建的问题的建议。

我已经包括这个在回答中一个较短的版本。

Answer 1:

我的2GB的问题是在多种不同类型的可怕马虎互换。 混合符号和无符号是问题。 基本上3GB位置我被传递给lseek的正在解释/化作-1GB的那样的位置和清楚lseek的didnt。 所以,我的坏。 那么傻废话编码。

再次感谢EOF,chux,P_L和乔纳森·莱弗勒谁贡献的所有信息,并导致我我已经创建的问题及其解决方案的建议。

再次感谢P_L的建议posix_fallocate(),并特别感谢NominalAnimal其持久性,他知道更好间接地使我认识到,我不能指望这反过来又导致我接受posix_fallocate会工作,所以改为使用它。

@p_l虽然解决了我的实际问题,你的答案是没有,我还是投了你的答案使用posix_fallocate该建议,但我没有足够的积分来做到这一点。



Answer 2:

首先,请尝试:

//Before any includes:
#define  _LARGEFILE64_SOURCE
#define  _FILE_OFFSET_BITS 64

如果还是不行,请更改lseeklseek64这样

lseek64(fd, 3221225472, SEEK_SET);

不是一个更好的选择lseek可能是posix_fallocate()

posix_fallocate(fd, 0, 3221225472);

前呼叫对mmap();

我建议保持定义,虽然:)



Answer 3:

这是一个测试程序,我创建( a2b.c ):

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

static void err_exit(const char *fmt, ...);

int main(void)
{
    char const filename[] = "big.file";
    int fd = open(filename, O_RDONLY);
    if (fd < 0)
        err_exit("Failed to open file %s for reading", filename);
    struct stat sb;
    fstat(fd, &sb);
    uint64_t size = sb.st_size;
    printf("File: %s; size %" PRIu64 "\n", filename, size);
    assert(size > UINT64_C(3) * 1024 * 1024 * 1024);
    off_t offset = UINT64_C(3) * 1024 * 1024 * 1024;
    if (lseek(fd, offset, SEEK_SET) < 0)
        err_exit("lseek failed");
    close(fd);
    _Static_assert(sizeof(size_t) > 4, "sizeof(size_t) is too small");
    size = UINT64_C(3) * 1024 * 1024 * 1024;
    void *space = malloc(size);
    if (space == 0)
        err_exit("failed to malloc %zu bytes", size);
    *((char *)space + size - 1) = '\xFF';
    printf("All OK\n");
    return 0;
}

static void err_exit(const char *fmt, ...)
{
    int errnum = errno;
    va_list args;
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    if (errnum != 0)
        fprintf(stderr, ": (%d) %s", errnum, strerror(errnum));
    putc('\n', stderr);
    exit(1);
}

当编译和在Mac(Mac OS X的10.9.2小牛,GCC 4.8.2,16吉布物理RAM)运行,使用命令行:

gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
    -Wold-style-definition -Werror a2b.c -o a2b

并具有创建big.file带:

dd if=/dev/zero of=big.file bs=1048576 count=5000

我得到了令人欣慰的输出:

File: big.file; size 5242880000
All OK

我不得不使用_Static_assert而非static_assert因为MAC <assert.h>头没有定义static_assert 。 当我编译-m32 ,静态断言触发。

当我与1个吉布虚拟物理存储器运行它的Ubuntu 13.10 64位VM(?或者是同义反复),我并不十分令人惊奇地得到了输出:

File: big.file; size 5242880000
failed to malloc 3221225472 bytes: (12) Cannot allocate memory

我恰好使用同一命令行来编译代码; 它编译OK的Linux上static_assert代替_Static_assert 。 输出ulimit -a显示,最大内存容量是无限的,但是这意味着“没有限制比通过机器上的虚拟内存量强加小”,而不是什么大。

请注意,我的汇编没有明确包括-m64但他们是自动的64位汇编。

你得到了什么? 可以dd创建的大文件? 是否代码编译? (如果您没有C11支持你的编译器,那么你需要用一个正常的“动态”断言,以取代静态断言,删除错误信息。)代码是否运行? 你会得到什么结果。



Answer 4:

下面是一个例子程序, example.c

/* Not required on 64-bit architectures; recommended anyway. */
#define  _FILE_OFFSET_BITS 64

/* Tell the compiler we do need POSIX.1-2001 features. */
#define  _POSIX_C_SOURCE 200112L

/* Needed to get MAP_NORESERVE. */
#define  _GNU_SOURCE

#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#ifndef   FILE_NAME
#define   FILE_NAME   "data.map"
#endif

#ifndef   FILE_SIZE
#define   FILE_SIZE   3221225472UL
#endif

int main(void)
{
    const size_t      size = FILE_SIZE;
    const char *const file = FILE_NAME;

    size_t            page;
    unsigned char    *data;

    int               descriptor;
    int               result;

    /* First, obtain the normal page size. */
    page = (size_t)sysconf(_SC_PAGESIZE);
    if (page < 1) {
        fprintf(stderr, "BUG: sysconf(_SC_PAGESIZE) returned an invalid value!\n");
        return EXIT_FAILURE;
    }

    /* Verify the map size is a multiple of page size. */
    if (size % page) {
        fprintf(stderr, "Map size (%lu) is not a multiple of page size (%lu)!\n",
                (unsigned long)size, (unsigned long)page);
        return EXIT_FAILURE;
    }

    /* Create backing file. */
    do {
        descriptor = open(file, O_RDWR | O_CREAT | O_EXCL, 0600);
    } while (descriptor == -1 && errno == EINTR);
    if (descriptor == -1) {
        fprintf(stderr, "Cannot create backing file '%s': %s.\n", file, strerror(errno));
        return EXIT_FAILURE;
    }

#ifdef FILE_ALLOCATE

    /* Allocate disk space for backing file. */
    do {
        result = posix_fallocate(descriptor, (off_t)0, (off_t)size);
    } while (result == -1 && errno == EINTR);
    if (result == -1) {
        fprintf(stderr, "Cannot resize and allocate %lu bytes for backing file '%s': %s.\n",
                (unsigned long)size, file, strerror(errno));
        unlink(file);
        return EXIT_FAILURE;
    }

#else

    /* Backing file is sparse; disk space is not allocated. */
    do {
        result = ftruncate(descriptor, (off_t)size);
    } while (result == -1 && errno == EINTR);
    if (result == -1) {
        fprintf(stderr, "Cannot resize backing file '%s' to %lu bytes: %s.\n",
                file, (unsigned long)size, strerror(errno));
        unlink(file);
        return EXIT_FAILURE;
    }

#endif

    /* Map the file.
     * If MAP_NORESERVE is not used, then the mapping size is limited
     * to the amount of available RAM and swap combined in Linux.
     * MAP_NORESERVE means that no swap is allocated for the mapping;
     * the file itself acts as the backing store. That's why MAP_SHARED
     * is also used. */
    do {
        data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NORESERVE,
                    descriptor, (off_t)0);
    } while ((void *)data == MAP_FAILED && errno == EINTR);
    if ((void *)data == MAP_FAILED) {
        fprintf(stderr, "Cannot map file '%s': %s.\n", file, strerror(errno));
        unlink(file);
        return EXIT_FAILURE;
    }

    /* Notify of success. */
    fprintf(stdout, "Mapped %lu bytes of file '%s'.\n", (unsigned long)size, file);
    fflush(stdout);

#if defined(FILE_FILL)
    memset(data, ~0UL, size);
#elif defined(FILE_ZERO)
    memset(data, 0, size);
#elif defined(FILE_MIDDLE)
    data[size/2] = 1; /* One byte in the middle set to one. */
#else

    /*
     * Do something with the mapping, data[0] .. data[size-1]
    */

#endif

    /* Unmap. */
    do {
        result = munmap(data, size);
    } while (result == -1 && errno == EINTR);
    if (result == -1)
        fprintf(stderr, "munmap(): %s.\n", strerror(errno));

    /* Close the backing file. */
    result = close(descriptor);
    if (result)
        fprintf(stderr, "close(): %s.\n", strerror(errno));

#ifndef FILE_KEEP

    /* Remove the backing file. */
    result = unlink(file);
    if (result)
        fprintf(stderr, "unlink(): %s.\n", strerror(errno));

#endif

    /* We keep the file. */
    fprintf(stdout, "Done.\n");
    fflush(stdout);

    return EXIT_SUCCESS;
}

要编译和运行,使用如

gcc -W -Wall -O3 -DFILE_KEEP -DFILE_MIDDLE example.c -o example
./example

上述将创建一个三千兆字节(1024 3)稀疏文件data.map ,并且在其设置的中间字节到1\x01 )。 该文件中的所有其它字节保持为零。 然后,您可以运行

du -h data.map

看到这样的稀疏文件实际上有多少需要在磁盘上,并且

hexdump -C data.map

如果你想验证文件内容是什么,我要求他们。

有几个编译时标志(宏)你可以用它来改变例子程序的行为方式:

  • '-DFILE_NAME="filename"'

    使用文件名filename ,而不是data.map 。 需要注意的是整个值单引号内定义,从而使外壳不解析双引号。 (双引号是宏值的一部分)。

  • '-DFILE_SIZE=(1024*1024*1024)'

    使用1024 3 = 1073741824字节映射,而不是默认3221225472.的如果表达式中包含特殊字符的外壳会试图评估,最好附上所有的单或双引号。

  • -DFILE_ALLOCATE

    实际分配的磁盘空间为整个映射。 默认情况下,稀疏文件代替。

  • -DFILE_FILL

    填充整个映射(unsigned char)(~0UL)典型地255。

  • -DFILE_ZERO

    清除全部映射到零。

  • -DFILE_MIDDLE

    在映射的中间字节设置为1。所有其他字节保持不变。

  • -DFILE_KEEP

    不要删除数据文件。 这是有益的探索映射实际上有多少数据需要在磁盘上; 使用如du -h data.map


在Linux中使用内存映射文件时,需要考虑三个关键限制:

  1. 文件大小限制

    像FAT(MS-DOS)旧文件系统不支持大文件,或稀疏文件。 如果数据集是稀疏的稀疏文件是有用的(含有大量孔); 在这种情况下,未固化的部分并不存储在磁盘上,并且简单地读作零。

    因为许多文件系统有问题的读取和写入大于2 31 -1个字节(2147483647个字节),当前的Linux内核内部限制每个单个操作到2 31 -1个字节。 读或写调用不会失败,它只是返回一个短数 。 我不知道任何文件系统类似的限制的llseek()系统调用,但由于C库是负责映射lseek()/lseek64()函数来正确的系统调用,这是完全可能的C库(而不是内核)限制的功能。 (在GNU C库的情况下和嵌入式GNU C库,这样的系统调用的映射依赖于编译时的标志。例如,参见man 7 feature_test_macrosman 2 lseekman 3 lseek64

    最后,文件中的位置的处理是在大多数Linux内核原子。 (补丁是上行的,但我不知道该版本包含他们。)这意味着如果多个线程使用相同的描述在修改文件位置的方法,可以在文件中的位置变得完全乱码。

  2. 内存限制

    默认情况下,文件支持的内存映射仍受到可用内存和交换限制。 也就是说,默认情况下mmap()行为是假设在内存压力,脏页被交换,不刷新到磁盘。 你需要使用Linux特定MAP_NORESERVE标志,以避免这些限制。

  3. 地址空间限制

    在32位Linux系统中,可用的地址空间到用户空间过程通常小于4吉布; 它是一个内核编译时选择。

    在64位Linux系统中,大的映射消耗显著数量的RAM,即使映射内容本身并不在又出现故障。通常,每个单页需要8个字节的元数据的存储器(“页表项”),或多个,根据架构。 使用4096字节的页面,这意味着0.1953125%的最低开销,并建立如TB级地图只需要在页面表结构的内存2G的!

    在Linux的许多64位系统支持庞大的网页 ,以避免开销。 在大多数情况下,大页面的用途有限,由于配置和调整和限制 。 内核也可能对什么是过程可以用一个巨大的页面映射做限制; 一个强大的应用程序将需要全面的回退到正常页面映射。

内核可能比征收资源可用性到用户空间进程更严格的限制。 运行bash -c 'ulimit -a'查看当前强加的限制。 (详细信息可在现有的ulimit在部分man bash-builtins )。



文章来源: can't create a file larger than 2GB on 64 bit linux system with mmap/malloc/open etc