Эффективная вставка блоков в середину файла

#linux #filesystems #mmap #memory-mapped-files #ext4

#linux #файловые системы #mmap #файлы с отображением в памяти #ext4

Вопрос:

Я ищу, по сути, эквивалент ext4 mremap() .

У меня есть большой mmap() общий файл, в котором я размещаю массивы, и массивы должны расти. Итак, я хочу увеличить размер первого массива в его текущем местоположении и сдвинуть все остальные массивы в файле и адресном пространстве, чтобы освободить место.

Если бы это была просто анонимная память, я мог бы использовать mremap() ее для перемещения по целым страницам за постоянное время, если я вставляю целое количество страниц памяти. Но это файл с дисковой поддержкой, поэтому данные должны перемещаться как в файле, так и в памяти.

На самом деле я не хочу читать, а затем переписывать целые блоки данных на физический диск и с него. Я хочу, чтобы данные оставались на диске в физических секторах, в которых они находятся, и побуждали файловую систему корректировать метаданные файла для вставки новых секторов, где мне нужно дополнительное пространство. Если мне нужно сохранить размер вставок, кратный размеру дискового сектора, зависящего от файловой системы, это нормально. Если мне в конечном итоге придется копировать O (N) ссылок на сектора или экстенты, чтобы освободить место для вставленного экстента, это нормально. Я просто не хочу, чтобы 2 гигабайта перемещались с диска и обратно на диск, чтобы вставить блок в середину файла объемом 4 гигабайта.

Как мне выполнить эффективную вставку, манипулируя метаданными файла? Действительно ли общий API для этого доступен в Linux? Или тот, который работает, если файловой системой является, например, ext4? Приведет ли write() вызов с указанием адреса источника в файле, отображенном в памяти, к тому виду эффективного сдвига, который я хочу, при правильных обстоятельствах?

Существует ли функция C или C API с семантикой «копировать байты отсюда туда и оставлять исходный код с неопределенным значением», которую я должен вызывать на случай, если эта оптимизация будет добавлена в стандартную библиотеку и ядро в будущем?

Я рассматривал возможность просто всегда выделять новые страницы в конце файла и отображать их в нужном месте в памяти. Но тогда мне нужно было бы каким-то образом восстановить эту серию сопоставлений при перезагрузке файла. Кроме того, сжатие структуры данных было бы нетривиальной проблемой. На этом этапе я бы писал менеджер страниц базы данных.

Ответ №1:

Я думаю, что я действительно, возможно, понял это.

Я пошел искать «linux make a file sparse», что привело меня к этому ответу на Unix amp; Linux Stack Exchange, в котором упоминался fallocate инструмент командной строки. fallocate Инструмент имеет --dig-holes опцию, которая превращает части файла, которые могут быть представлены отверстиями, в отверстия.

Затем я отправился на поиски «fallocate dig holes», чтобы узнать, как это работает, и получил fallocate справочную страницу. Я заметил, что это также предлагает способ вставить отверстие определенного размера:

 -i, --insert-range
              Insert a hole of length bytes from offset, shifting existing
              data.
 

Если это может сделать инструмент командной строки, Linux может это сделать, поэтому я изучил исходный код fallocate, который вы можете найти на Github:

 case 'i':
    mode |= FALLOC_FL_INSERT_RANGE;
    break;
 

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

Мне не сразу понятно, как это взаимодействует с текущими страницами, отображенными в памяти, но я думаю, что смогу с этим работать.