Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 为什么我们可以分配一个1 PB(10^15)数组并访问最后一个元素,但却可以';你不能释放它吗?_C_Linux_Malloc_Mmap_Virtual Address Space - Fatal编程技术网

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

C 为什么我们可以分配一个1 PB(10^15)数组并访问最后一个元素,但却可以';你不能释放它吗?,c,linux,malloc,mmap,virtual-address-space,C,Linux,Malloc,Mmap,Virtual Address Space,众所周知: 默认情况下,Linux遵循乐观的内存分配策略。 这意味着,当malloc()返回非NULL时,不保证 内存确实可用。万一结果是 系统内存不足,一个或多个进程将被 杀手 通过使用malloc(Petabyte),我们可以成功地分配1petabyte的VMA(虚拟内存区域): 我们可以成功地访问(存储/加载)到PB的最后一个成员,但是为什么在free((void*)ptr)上会出现错误 注: 1000^5 PB字节 1024^5像素字节-我使用它 因此,如果我们真的想分配超过RAM+

众所周知:

默认情况下,Linux遵循乐观的内存分配策略。 这意味着,当malloc()返回非NULL时,不保证 内存确实可用。万一结果是 系统内存不足,一个或多个进程将被 杀手

通过使用
malloc(Petabyte),我们可以成功地分配1petabyte的VMA(虚拟内存区域)

我们可以成功地访问(存储/加载)到PB的最后一个成员,但是为什么在
free((void*)ptr)上会出现错误

注:

  • 1000^5 PB字节
  • 1024^5像素字节-我使用它
因此,如果我们真的想分配超过RAM+交换的内存,并解决
过多限制内存的问题,那么我们可以在Windows或Linux上使用分配内存,例如:

  • 16 TiB(16*2^40字节)然后我们可以使用标称动物答案中的示例:
  • 127 TiB(127*2^40字节),然后我们可以使用带有标志的
    mmap()
    ,如
    MAP\u NORESERVE | MAP\u PRIVATE | MAP\u ANONYMOUS
    fd=-1

我认为您的问题在于
malloc()
没有将
long int
作为其参数。它需要
大小\u t

将代码更改为将
petabyte
定义为
size\t
后,程序不再从malloc返回指针。相反,它失败了

我认为您的数组访问设置PB-1到10写的远远超出了malloc返回的数组。这就是坠机事件

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

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

long long int petabyte = 1024LL * 1024LL * 1024LL * 1024LL * 1024LL;
size_t ptest = petabyte;
printf("petabyte %lld %lu\n", petabyte, ptest);
如果我在64位模式下编译,它将无法malloc1 PB。如果我在32位模式下编译成功,它将占用0字节,然后尝试在其数组和segfaults之外进行写入。

(这不是一个答案,但对于在Linux中处理大型数据集的任何人来说都是一个重要注意事项)

在Linux中,这不是使用非常大的数据集(以TB为单位)的方式

当您使用
malloc()
mmap()
(GNU C库将在内部使用
mmap()
进行大型分配)来分配私有内存时,内核将大小限制为(理论上)可用RAM和交换的大小,乘以过限系数

简单地说,我们知道可能需要交换大于RAM的数据集,因此当前交换的大小将影响允许的分配大小

为了解决这个问题,我们创建了一个用作数据“交换”的文件,并使用
map\u 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;
}
并在没有参数的情况下运行它以查看用法

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

在我的机器上,在x86-64上运行4.2.0-42-generic#49~14.04.1-Ubuntu SMP内核,在ext4文件系统上,我无法映射一个完整的PB。最大值似乎约为17592186040320字节(244-4096)--16个TiB-4 KiB--,总计4294967296页4096字节(每个232页212字节)。这似乎是ext4文件系统强加的限制,因为失败发生在
ftruncate()
调用中(甚至在尝试映射之前)

(在tmpfs上,我可以获得大约140187732541440字节或127.5TIB,但这只是一个噱头,因为tmpfs由RAM和交换支持,而不是一个实际的存储设备。因此,这不是真正的大数据工作的选项。我似乎记得xfs可以处理非常大的文件,但我太懒了,无法格式化分区,甚至无法查找规范;我不这么认为。)我想任何人都会读到这篇文章,尽管在过去十年左右,这里的信息对我非常有用。)

下面是该示例运行在我的机器上的样子(使用bashshell):


$。/mapfile datafile$[(1任何您符合
volatile
条件的原因?这很可能是无稽之谈,并且不利于优化。@Olaf
volatile
避免没有内存访问的优化,并将在寄存器中工作。为了表明我们确实在使用内存。也就是说,只是吹毛求疵,
(int)
..%d\n”(int)(ptr[petabyte-1LL])中不需要强制转换;
是隐式的。挑剔。您的
petabyte
注释是错误的。这不是10^15。它是2^50。(如果我正确计算了位)。启用警告,尤其是
-Wconversion
。不同意“…您的程序不再从malloc返回指针,而是失败。“如果
1024LL*1024LL*1024LL*1024LL*1024LL*1024LL*1024LL
超过
SIZE\u MAX
,则
SIZE\u t petabyte
的预期值将为0。当然
malloc(0)
没有失败。
malloc(0)
返回指针或
NULL
,两者都是指针的有效值,不表示OOM。错误出现在后面的代码中,该代码试图取消引用
ptr
@chux:在我的64位机器上,它拒绝分配PB,没有mat
long long int petabyte = 1024LL * 1024LL * 1024LL * 1024LL * 1024LL;
size_t ptest = petabyte;
printf("petabyte %lld %lu\n", petabyte, ptest);
#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;
}
gcc -Wall -O2 mapfile.c -o mapfile
$ ./mapfile datafile $[(1<<44)-4096]
Created file 'datafile' to back a 17592186040320-byte mapping at 0x6f3d3e717000 successfully.
Mapping accessed successfully.
All done.