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

#c #linux #mmap #ace

#c #linux #mmap #туз

Вопрос:

Контекст: я использую файл с отображением памяти в своем коде, созданном с помощью 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, '', 256);

        /* Issue not replicating */
        memset(test, '', 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 filen");
        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 failuren");
        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 ГБ дискового пространства будут освобождены. Не знаю причины

Комментарии:

1. Структуры C не могут иметь функций-членов.

2. @JohnBollinger, проблема повторяется, даже если я удаляю функцию-член

3. Даже без функций-членов дело в том, что это не вопрос C. ACE — это инструментарий C , и другие аспекты представленного кода также специфичны для C . Действительно, поскольку программа использует ACE для сопоставления файла, в отличие mmap от прямого вызова, вопрос в его нынешнем виде специфичен для ACE.

4. Когда вы говорите о фактическом «размере файла», как вы это измеряете? ls ?

5. Я использую команды du и stat для проверки размера файла

Ответ №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, '', 256);

        /* Issue not replicating */
        memset(test, '', 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 amp;amp; write (argc == 5).

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

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

Комментарии:

1. В моем случае создается разреженный файл. Проблема в том, что количество дисковых блоков, выделенных для файла, не соответствует количеству дисковых блоков, потребляемых из файловой системы. Предположим, максимальный размер, заданный файлу, составляет 2 ГБ. Файл занял 521 МБ дискового пространства, как показано в выводе du -sh и stat. Но, если я вижу дисковое пространство на fs с использованием df -h, то это показывает, что потребляется ~ 3 ГБ, что намного больше, чем назначенные 2 ГБ. В чем может быть причина этого? Примечание: проблема воспроизводима и в коде, которым вы поделились, а также после строки комментария memset(test, '', TEST_BUFF_SIZE); и argc = 4 или 5

2. Умножьте РАЗМЕР ТЕСТА __BUFF_ на 10 и разделите свой arg1 на 10; Я думаю, что размер выделения вашей файловой системы — это проблема. Извините за другое, мой ubuntu 20 этого не делает.

3. Я использую файловую систему XFS. Можете ли вы поделиться, какую файловую систему вы используете??

4. xfs4 в ubuntu 20. У меня была другая мысль; возможно ли, что ваш linux создает огромные страницы? Здесь есть некоторая документация: oracle-base.com/articles/linux /… , обратите внимание на то, что нужно форсировать огромные страницы.