Почему мы можем выделить массив размером 1 ПБ (10 ^ 15) и получить доступ к последнему элементу, но не можем его освободить?

Как известно: http://linux.die.net/man/3/malloc

  

По умолчанию Linux следует оптимистичной стратегии выделения памяти.   Это означает, что когда malloc () возвращает не NULL, нет гарантии   что память действительно доступна. Если окажется, что   системе не хватает памяти, один или несколько процессов будут убиты   Убийца ООМ.

И мы можем успешно выделить 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;
}

Результат:

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

И мы можем успешно получить доступ (сохранить /загрузить) к последнему члену петабайта, но почему мы получаем ошибку в free((void*)ptr);?

Примечание. https://en.wikipedia.org/wiki/Petabyte

  • 1000 ^ 5 ПБ петабайт
  • 1024 ^ 5 PiB pebibyte - Я использую его

Так что если мы хотим выделить больше, чем RAM + swap, и обойти ограничение overcommit_memory, то мы можем выделить память, используя VirtualAllocEx() в Windows или mmap() в Linux, например:

19 голосов | спросил Alex 25 J000000Monday16 2016, 15:49:17

2 ответа


0

Я считаю, что ваша проблема в том, что malloc() не принимает long long int в качестве аргумента. Требуется size_t.

После изменения кода определите petabyte как size_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);

Если я скомпилирую в 64-битном режиме, он не сможет разместить 1 петабайт. Если я скомпилирую в 32-битном режиме, он успешно выдает 0 байтов, затем попытается записать вне своего массива и сбоев.

ответил Zan Lynx 25 J000000Monday16 2016, 15:57:37
0

(Это не ответ, а важное замечание для всех, кто работает с большими наборами данных в Linux)

Это не то, как вы используете очень большие - порядка терабайтов и наборы данных в Linux.

Когда вы используете malloc() или mmap() (библиотека GNU C будет использовать mmap() для больших выделений в любом случае) для выделения частной памяти, ядро ​​ограничивает размер до размера (теоретически) доступной оперативной памяти и SWAP, умноженных на коэффициент перегрузок.

Проще говоря, мы знаем, что наборы данных, превышающие объем ОЗУ, возможно, придется поменять местами, поэтому размер текущего свопа будет влиять на то, насколько разрешено выделение больших объемов.

Чтобы обойти это, мы создадим файл, который будет использоваться как «своп» для данных, и отобразим его, используя MAP_NORESERVE флаг. Это говорит ядру, что мы не хотим использовать стандартный обмен для этого отображения. (Это также означает, что если по какой-либо причине ядро ​​не может получить новую страницу поддержки, приложение получит сигнал SIGSEGV и умрет .)

Большинство файловых систем в Linux поддерживают разреженные файлы. Это означает, что вы можете иметь файл размером в терабайт, который занимает всего несколько килобайт фактического дискового пространства, если большая часть его содержимого не записана (и, следовательно, равна нулю). (Создавать разреженные файлы легко; вы просто пропускаете длинную серию нулей. Перфорация более сложна, поскольку при записи нулей используется обычное дисковое пространство, вместо этого нужно использовать другие методы.)

Вот пример программы, которую вы можете использовать для исследования, 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;
}

Скомпилируйте его, например,

.
gcc -Wall -O2 mapfile.c -o mapfile

и запустите его без аргументов, чтобы увидеть использование.

Программа просто устанавливает отображение (настроенное на кратное текущему размеру страницы) и получает доступ к первым двум и последним двум байтам отображения.

На моем компьютере, работающем под управлением 4.2.0-42-generic # 49 ~ 14.04.1-Ubuntu SMP kernel на x86-64, в файловой системе ext4 я не могу отобразить полный петабайт. Максимум составляет около 17 592 186 040 320 байт (2 44 -4096) - 16 ТиБ - 4 КиБ - что составляет 4 294 967 296 страниц из 4096 байт (2 32 страниц). из 2 12 байтов каждый). Похоже, что ограничение накладывается файловой системой ext4, так как сбой происходит в вызове ftruncate() (еще до того, как сопоставление будет пробовано).

(В tmpfs я могу получить до 140 187 732 541 440 байт или 127,5 ТБ, но это просто уловка, поскольку tmpfs поддерживается ОЗУ и подкачкой, а не реальным устройством хранения. Так что это не вариант для работы с большими данными Кажется, я вспоминаю, что xfs подойдет для действительно больших файлов, но мне лень форматировать раздел или даже искать спецификации; я не думаю, что кто-то на самом деле прочтет этот пост, хотя информация здесь очень полезно для меня в течение последнего десятилетия или около того.)

Вот как выглядит этот пример выполнения на моем компьютере (с использованием оболочки Bash):

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

.

ответил Nominal Animal 25 J000000Monday16 2016, 21:11:33

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132