为什么我们可以分配1 PB(10 ^ 15)数组并访问最后一个元素,但是不能释放它?

Ale*_*lex 19 c linux malloc mmap virtual-address-space

众所周知:http://linux.die.net/man/3/malloc

默认情况下,Linux遵循乐观的内存分配策略.这意味着当malloc()返回非NULL时,无法保证内存确实可用.如果事实证明系统内存不足,那么一个或多个进程将被OOM杀手杀死.

和我们能够成功地通过使用分配1个拍字节VMA(虚拟存储区)的malloc(petabyte);:http://ideone.com/1yskmB

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

int main(void) {

    long long int petabyte = 1024LL * 1024LL * 1024LL * 1024LL * 1024LL;    // 2^50
    printf("petabyte %lld \n", petabyte);

    volatile char *ptr = (volatile char *)malloc(petabyte);
    printf("malloc() - success, ptr = %p \n", ptr);

    ptr[petabyte - 1LL] = 10;
    printf("ptr[petabyte - 1] = 10; - success \n");

    printf("ptr[petabyte - 1] = %d \n", (int)(ptr[petabyte - 1LL]));

    free((void*)ptr);   // why the error is here?
    //printf("free() - success \n");

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

结果:

Error   time: 0 memory: 2292 signal:6
petabyte 1125899906842624 
malloc() - success, ptr = 0x823e008 
ptr[petabyte - 1] = 10; - success 
ptr[petabyte - 1] = 10 
Run Code Online (Sandbox Code Playgroud)

我们可以成功访问(存储/加载)到petabyte的最后一个成员,但为什么我们会收到错误free((void*)ptr);

注意:https://en.wikipedia.org/wiki/Petabyte

  • 1000 ^ 5 PB petabyte
  • 1024 ^ 5 PiB pebibyte - 我用它

所以,如果我们想要分配RAM +交换以及解决overcommit_memory限制,那么我们可以通过VirtualAllocEx()在Windows或mmap()Linux上使用来分配内存,例如:

Zan*_*ynx 23

我相信你的问题是malloc()long long int以其为参数.需要一个size_t.

更改代码以定义petabytesize_t程序后,不再返回malloc指针.它失败了.

我认为你的数组访问设置petabyte-1到10写得很远,远远超出了malloc返回的数组.这就是崩溃.

调用函数时始终使用正确的数据类型.

使用此代码查看发生了什么:

long long int petabyte = 1024LL * 1024LL * 1024LL * 1024LL * 1024LL;
size_t ptest = petabyte;
printf("petabyte %lld %lu\n", petabyte, ptest);
Run Code Online (Sandbox Code Playgroud)

如果我在64位模式下编译它无法malloc 1 petabyte.如果我在32位模式下编译它成功地mallocs 0字节,然后尝试在其数组和segfaults之外写入.

  • 不同意"......你的程序不再返回来自malloc的指针.它失败了." 如果`1024LL*1024LL*1024LL*1024LL*1024LL`超过`SIZE_MAX`,`size_t petabyte`的期望值将为0.当然`malloc(0)`没有失败.`malloc(0)`返回一个指针或'NULL`,两者都是指针的有效值,并不表示OOM.错误发生在后来的代码中,试图取消引用`ptr`. (2认同)
  • 没有_signed_整数换行.`size_t`是无符号类型.无论其位宽如何,都可以很好地定义2 ^ 50到`size_t`.将`size_t`作为50位或更窄的类型,`size_t petabyte = 1024LL ...`将为0. (2认同)
  • 6.3.1.3有符号和无符号整数2"如果新类型是无符号的,则通过重复加或减一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内为止".它不是_signed_溢出. (2认同)

Nom*_*mal 9

(这不是一个答案,但是关于在Linux中使用大型数据集的任何人的重要说明)

这不是你在Linux中使用非常大的数据 - 大约数TB和数据集.

当您使用malloc()mmap()(GNU C库将在mmap()内部使用大量分配)来分配专用内存时,内核将大小限制为(理论上)可用RAM和SWAP的大小,乘以过度使用因子.

简而言之,我们知道可能必须换出大于RAM的数据集,因此当前交换的大小将影响允许的大量分配.

为了解决这个问题,我们创建了一个文件,用作数据的"交换",并使用MAP_NORESERVE标志映射它.这告诉内核我们不想为这个映射使用标准交换.(这也意味着,如果由于任何原因,内核无法获得新的支持页面,应用程序将获得SIGSEGV信号并死亡.)

Linux中的大多数文件系统都支持稀疏文件.这意味着你可以拥有一个TB大小的文件,如果它的大部分内容都没有写入(因此为零),只需要几千字节的实际磁盘空间.(创建稀疏文件很简单;只需跳过长时间的零.打孔更难,因为写零会使用正常的磁盘空间,而是需要使用其他方法.)

这是一个可用于探索的示例程序mapfile.c:

#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    const char    *filename;
    size_t         page, size;
    int            fd, result;
    unsigned char *data;
    char           dummy;

    if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s MAPFILE BYTES\n", argv[0]);
        fprintf(stderr, "\n");
        return EXIT_FAILURE;
    }

    page = sysconf(_SC_PAGESIZE);
    if (page < 1) {
        fprintf(stderr, "Unknown page size.\n");
        return EXIT_FAILURE;
    }

    filename = argv[1];
    if (!filename || !*filename) {
        fprintf(stderr, "No map file name specified.\n");
        return EXIT_FAILURE;
    }

    if (sscanf(argv[2], " %zu %c", &size, &dummy) != 1 || size < 3) {
        fprintf(stderr, "%s: Invalid size in bytes.\n", argv[2]);
        return EXIT_FAILURE;
    }

    if (size % page) {
        /* Round up to next multiple of page */
        size += page - (size % page);
        fprintf(stderr, "Adjusted to %zu pages (%zu bytes)\n", size / page, size);
    }

    do {
        fd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
    } while (fd == -1 && errno == EINTR);
    if (fd == -1) {
        fprintf(stderr, "Cannot create %s: %s.\n", filename, strerror(errno));
        return EXIT_FAILURE;
    }

    do {
        result = ftruncate(fd, (off_t)size);
    } while (result == -1 && errno == EINTR);
    if (result == -1) {
        fprintf(stderr, "Cannot resize %s: %s.\n", filename, strerror(errno));
        unlink(filename);
        close(fd);
        return EXIT_FAILURE;
    }

    data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NORESERVE, fd, 0);
    if ((void *)data == MAP_FAILED) {
        fprintf(stderr, "Mapping failed: %s.\n", strerror(errno));
        unlink(filename);
        close(fd);
        return EXIT_FAILURE;
    }

    fprintf(stderr, "Created file '%s' to back a %zu-byte mapping at %p successfully.\n", filename, size, (void *)data);

    fflush(stdout);
    fflush(stderr);

    data[0] = 1U;
    data[1] = 255U;

    data[size-2] = 254U;
    data[size-1] = 127U;

    fprintf(stderr, "Mapping accessed successfully.\n");

    munmap(data, size);
    unlink(filename);
    close(fd);

    fprintf(stderr, "All done.\n");
    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

使用例如编译它

gcc -Wall -O2 mapfile.c -o mapfile
Run Code Online (Sandbox Code Playgroud)

并在没有参数的情况下运行它以查看用法.

程序只是设置一个映射(调整为当前页面大小的倍数),并访问映射的前两个和后两个字节.

在我的机器上,在x86-64上运行4.2.0-42通用#49~14.04.1-Ubuntu SMP内核,在ext4文件系统上,我无法映射完整的PB.最大似乎是大约17,592,186,040,320字节(2 44 -4096) - 16 TiB - 4 KiB - ,它来自4,294,967,296页4096字节(2 32页,每页2 12字节).看起来这个限制是由ext4文件系统强加的,因为ftruncate()调用失败(甚至在尝试映射之前).

(在tmpfs的,我可以得到高达约140,187,732,541,440字节或127.5的TiB,但是这只是一个噱头,因为tmpfs的是RAM和交换,而不是实际的存储设备的支持,所以它不是一个真正的大数据工作的选项.我似乎回想起xfs可以用于非常大的文件,但我懒得格式化分区甚至查看规格;我认为没有人会真正阅读这篇文章,即使这里的信息对我来说非常有用在过去十年左右.)

以下是该示例运行在我的机器上的显示方式(使用Bash shell):

$ ./mapfile datafile $[(1<<44)-4096]
Created file 'datafile' to back a 17592186040320-byte mapping at 0x6f3d3e717000 successfully.
Mapping accessed successfully.
All done.
Run Code Online (Sandbox Code Playgroud)

.