Подтвердить что ты не робот

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

Как известно: 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;
}

Результат:

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

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

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

  • 1000 ^ 5 PB petabyte
  • 1024 ^ 5 PiB pebibyte - Я использую его

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

  • 16 TiB (16 * 2 ^ 40 байт), то мы можем использовать пример из ответа "Номинальный корень": qaru.site/info/279523/...
  • 127 TiB (127 * 2 ^ 40 байт), то мы можем использовать mmap() с флагами MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS и fd=-1: http://coliru.stacked-crooked.com/a/c69ce8ad7fbe4560
4b9b3361

Ответ 1

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

После изменения кода для определения petabyte в качестве size_t ваша программа больше не возвращает указатель из malloc. Это не работает.

Я думаю, что настройки доступа к массиву петабайт-1 до 10 записываются далеко, далеко за пределами массива malloc. Это катастрофа.

Всегда всегда используйте правильные типы данных при вызове функций.

Используйте этот код, чтобы узнать, что происходит:

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

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

Ответ 2

(Это не ответ, а важная заметка для любого, кто работает с большими наборами данных в 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

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

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

На моей машине, запущенной ядро ​​# 49 ~ 14.04.1-Ubuntu SMP на базе x86-64, в файловой системе ext4, я не могу отобразить полный петабайт. Максимум, по-видимому, составляет около 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 поддерживается оперативной памятью и свопом, а не фактическим устройством хранения данных. Таким образом, это не вариант для реальной работы с большими данными Я, кажется, помню, что xfs будет работать для действительно больших файлов, но я слишком ленив, чтобы отформатировать раздел или даже посмотреть спецификации, я не думаю, что кто-то действительно прочитает этот пост, хотя информация здесь была очень полезный для меня в течение последнего десятилетия или около того.)

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

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

.