← На главную

Отображения файла в память под Linux с помощью mmap

В прошлый раз мы поговорили об отображении файлов в память при помощи WinAPI, а сегодня разберемся, как то же самое делается под nix-системами, в частности Linux и MacOS. Проверить код под FreeBSD я поленился, но по идее все должно работать и в этой операционной системе. Повторюсь – я почти уверен, что многие читатели сего блога уже знакомы с отображением файлов в память, поэтому пост предназначен для всех остальных читателей.

Укажем необходимые инклуды и объявим структуру FileMapping, хранящую файловый дескриптор, размер файла и указатель на участок памяти с отображением:

#include <sys/mman.h> #include <fcntl.h> #include <sys/stat.h> #include <unistd.h> struct FileMapping { int fd; size_t fsize; unsigned char* dataPtr; };

Рассмотрим чтение из файла с использованием отображения.

Открываем файл на чтение:

int fd = open(fname, O_RDONLY, 0); if(fd < 0) { std::cerr << "fileMappingCreate - open failed, fname = " << fname << ", " << strerror(errno) << std::endl; return nullptr; }

Узнаем размер файла:

struct stat st; if(fstat(fd, &st) < 0) { std::cerr << "fileMappingCreate - fstat failed, fname = " << fname << ", " << strerror(errno) << std::endl; close(fd); return nullptr; } size_t fsize = (size_t)st.st_size;

Вызовом mmap создаем отображение файла в память:

unsigned char* dataPtr = (unsigned char*)mmap(nullptr, fsize, PROT_READ, MAP_PRIVATE, fd, 0); if(dataPtr == MAP_FAILED) { std::cerr << "fileMappingCreate - mmap failed, fname = " << fname << ", " << strerror(errno) << std::endl; close(fd); return nullptr; }

Наконец, заполняем структуру FileMapping и возвращаем указатель на нее в качестве результата:

FileMapping * mapping = (FileMapping *)malloc(sizeof(FileMapping)); if(mapping == nullptr) { std::cerr << "fileMappingCreate - malloc failed, fname = " << fname << std::endl; munmap(dataPtr, fsize); close(fd); return nullptr; } mapping->fd = fd; mapping->fsize = fsize; mapping->dataPtr = dataPtr; return mapping;

Теперь по адресу mapping->dataPtr мы можем читать mapping->fsize байт содержимого файла.

Как всегда, не забываем освобождать за собой ресурсы, когда они становятся ненужны:

munmap(mapping->dataPtr, mapping->fsize); close(mapping->fd); free(mapping);

Вот и все! Сожалею, если вы ожидали чего-то более сложного :) Полную версию исходного кода вы найдете здесь.

Те, кому представленный материал показался слишком простым, могут в качестве домашнего задания сделать следующее:

  • Взять одну из *BSD систем и проверить, работает ли код на ней;
  • Переписать пример так, чтобы файл можно было не только читать, но и писать в него;
  • Выяснить, можно ли менять размер файла, отображенного в память;
  • Выяснить, что будет, если создать отображение файла, а затем записать в него что-то через обычный вызов write;
  • Погуглить на тему использования mmap в качестве IPC, написать соответствующий пример;

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

Дополнение: Обратите также внимание на системные вызовы mlock / munlock, msync, madvise и mremap. В определенных типах приложений (например, СУБД) они могут быть весьма полезны.