Файл с отображением памяти Linux занимает больше места на диске, чем ожидалось

Контекст: я использую файл с отображением памяти в своем коде, созданном с помощью ACE_Mem_Map. Замечено, что файл отображения памяти занимает больше места на диске, чем ожидалось.

Сценарий. У меня есть структура, содержащая массив символов размером 15 КБ. Я создал файл карты памяти для массива этой структуры с размером файла ~ 2 ГБ.

  1. Если я попытаюсь получить доступ к нескольким байтам массива символов (скажем, 256), то потребляемый размер файла отображается как 521 МБ, но фактическое использование диска, показанное файловой системой (с использованием df -h), превышает 3 ГБ.
  2. Если я получаю доступ ко всем байтам памяти, то размер файла и использование диска отображаются как 2 ГБ.

Окружающая среда: ОС: Oracle Linux 7.3 Версия ядра: 3.10.0/4.1.12

Код:

#include<ace/Mem_Map.h>
#include <stdio.h>

#define TEST_BUFF_SIZE 15*1024

typedef struct _test_struct_ {
    char test[TEST_BUFF_SIZE];

    _test_struct_() {
        reset();
    }

    void reset() {
        /* Issue replicating */
        memset(test, '\0', 256);

        /* Issue not replicating */
        memset(test, '\0', TEST_BUFF_SIZE);
    }
}TestStruct_t;

int main(int argc, char *argv[]) {

    if(3 != argc) {
        printf("Usage: %s <num of blocks> <filename>\n",
                argv[0]);
        return -1;
    }
    ACE_Mem_Map map_buf_;

    size_t num_of_blocks = strtoull(argv[1], NULL, 10);

    size_t MAX_SIZE = num_of_blocks*sizeof(TestStruct_t);

    char* mmap_file_name = argv[2];

    printf("num_of_blocks[%llu], sizeof(TestStruct_t)[%llu], MAX_SIZE[%llu], mmap_file_name[%s]\n",
            num_of_blocks,
            sizeof(TestStruct_t),
            MAX_SIZE,
            mmap_file_name);

    TestStruct_t *base_addr_;

    ACE_HANDLE fp_ = ACE_OS::open(mmap_file_name,O_RDWR|O_CREAT,
            ACE_DEFAULT_OPEN_PERMS,0);

    if (fp_ == ACE_INVALID_HANDLE)
    {
        printf("Error opening file\n");
        return -1;
    }
    map_buf_.map(fp_,MAX_SIZE,PROT_WRITE,MAP_SHARED);

    base_addr_ = (TestStruct_t*)map_buf_.addr();
    if (base_addr_ == MAP_FAILED)
    {
        printf("Map init failure\n");
        ACE_OS::close(fp_);
        return -1;
    }

    printf("map_buf_ size[%llu]\n",
            map_buf_.size());

    for(size_t i = 0; i < num_of_blocks; i++) {
        base_addr_[i].reset();
    }

    return 0;
}

Кто-нибудь может объяснить, почему происходит сценарий 1??

Примечание. В сценарии 1, если я сделаю копию сгенерированного файла mmap, а затем удалю эту копию, то дополнительные 2,5 ГБ дискового пространства будут освобождены. Не знаю причину


person Aman Gupta    schedule 25.11.2020    source источник
comment
Структуры C не могут иметь функций-членов.   -  person John Bollinger    schedule 25.11.2020
comment
@JohnBollinger, проблема повторяется, даже если я удаляю функцию-член   -  person Aman Gupta    schedule 25.11.2020
comment
Дело в том, что даже без функций-членов это не вопрос C. ACE — это инструментарий C++, и другие аспекты представленного кода также специфичны для C++. Действительно, поскольку программа использует ACE для сопоставления файла, а не напрямую вызывает mmap, вопрос в его нынешнем виде специфичен для ACE.   -  person John Bollinger    schedule 25.11.2020
comment
Когда вы говорите о реальном размере файла, как вы его измеряете? ls?   -  person John Bollinger    schedule 25.11.2020
comment
Я использую команду du и stat для проверки размера файла.   -  person Aman Gupta    schedule 26.11.2020


Ответы (1)


Я «обновил» вашу программу почти до C и минус все, что есть ACE, и получил это:

$ ./a.out 32 fred
num_of_blocks[32], sizeof(TestStruct_t)[15360], MAX_SIZE[491520], mmap_file_name[fred]
Bus error: 10

Что вполне ожидаемо. Mmap не увеличивает размер сопоставленного файла, поэтому генерирует ошибку адреса, когда вы пытаетесь сослаться на незаполненную часть. Итак, ответ таков: что бы ни делал ACE.map, он, скорее всего, вызывает что-то вроде ftruncate(2) для расширения файла до размера, который вы указываете в качестве параметра. @John Bollinger намекает на это, спрашивая how are you measuring that: ls или du. Вы должны использовать последний. Во всяком случае, почти C-версия:

#include <sys/mman.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

#define TEST_BUFF_SIZE 15*1024

typedef struct _test_struct_ {
    char test[TEST_BUFF_SIZE];

    _test_struct_() {
        reset();
    }

    void reset() {
        /* Issue replicating */
        memset(test, '\0', 256);

        /* Issue not replicating */
        memset(test, '\0', TEST_BUFF_SIZE);
    }
}TestStruct_t;

int main(int argc, char *argv[]) {

    if(argc < 3) {
        printf("Usage: %s <num of blocks> <filename>\n",
                argv[0]);
        return 1;
    }
    void *buf;

    size_t num_of_blocks = strtoull(argv[1], NULL, 10);

    size_t MAX_SIZE = num_of_blocks*sizeof(TestStruct_t);

    char* mmap_file_name = argv[2];

    printf("num_of_blocks[%zu], sizeof(TestStruct_t)[%zu], MAX_SIZE[%zu], mmap_file_name[%s]\n",
            num_of_blocks,
            sizeof(TestStruct_t),
            MAX_SIZE,
            mmap_file_name);


    int fp = open(mmap_file_name,O_RDWR|O_CREAT,0666);

    if (fp == -1)
    {
        perror("Error opening file");
        return 1;
    }
    /*SOMETHING CLEVER*/
    switch (argc) {
    case 3:
        break;
    case 4:
        if (ftruncate(fp, MAX_SIZE) != 0) {
            perror("ftruncate");
            return 1;
        }
        break;
    case 5:
        if (lseek(fp, MAX_SIZE-1, SEEK_SET) != MAX_SIZE-1 ||
            write(fp, "", 1) != 1) {
            perror("seek,write");
            return 1;
        }
    }
    void *b = mmap(0, MAX_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fp, 0);
    if (b == MAP_FAILED)
    {
        perror("Map init failure");
        return 1;
    }
    TestStruct_t *base_addr = (TestStruct_t *)b;

    for(size_t i = 0; i < num_of_blocks; i++) {
        base_addr[i].reset();
    }

    return 0;
}

Бит SOMETHING CLEVER позволяет либо работать с пустым файлом (argc == 3), либо увеличивать его с помощью ftruncate (argc == 4), либо увеличивать его с помощью lseek && write (argc == 5).

В системах UNIX-y ftruncate может резервировать или не резервировать место для вашего файла; удлиненный файл без зарезервированного места называется разреженным. Почти повсеместно lseek && write создаст разреженный файл, если только ваша система не поддерживает это.

Разреженный файл будет выделять фактические блоки диска по мере того, как вы записываете в него, однако, если он выйдет из строя, он передаст сигнал, а предварительно выделенный - нет. Ваш цикл внизу проходит весь экстент, поэтому файл всегда будет расти; уменьшите этот цикл, и вы увидите, влияют ли параметры на вашу систему.

person mevets    schedule 25.11.2020
comment
В моем случае создается разреженный файл. Проблема в том, что количество дисковых блоков, выделенных для файла, не соответствует количеству дисковых блоков, потребляемых файловой системой. Предположим, максимальный размер файла составляет 2 ГБ. Файл занял 521 МБ дискового пространства, как показано в выводе du -sh и stat. Но если я вижу дисковое пространство на fs с помощью df -h, то это показывает, что потребляется ~ 3 ГБ, что намного больше, чем назначенные 2 ГБ. Что может быть причиной этого? Примечание. Проблема воспроизводится и с кодом, которым вы поделились, после комментирования строки memset(test, '\0', TEST_BUFF_SIZE); и argc=4 или 5. - person Aman Gupta; 26.11.2020
comment
Умножьте TEST__BUFF_SIZE на 10 и разделите arg1 на 10; Я думаю, что проблема в размере вашей файловой системы. Извините за другое, мой Ubuntu 20 этого не делает. - person mevets; 26.11.2020
comment
Я использую файловую систему XFS. Можете ли вы поделиться, какую файловую систему вы используете ?? - person Aman Gupta; 30.11.2020
comment
xfs4 на Ubuntu 20. У меня была другая мысль; возможно ли, что ваш Linux заставляет огромные страницы? Здесь есть некоторая документация: oracle- base.com/articles/linux/ обратите внимание на форсирование огромных страниц. - person mevets; 30.11.2020