Отображения файла в память под Linux с помощью mmap
26 октября 2015
В прошлый раз мы поговорили об отображении файлов в память при помощи WinAPI, а сегодня разберемся, как то же самое делается под nix-системами, в частности Linux и MacOS. Проверить код под FreeBSD я поленился, но по идее все должно работать и в этой операционной системе. Повторюсь — я почти уверен, что многие читатели сего блога уже знакомы с отображением файлов в память, поэтому пост предназначен для всех остальных читателей.
Укажем необходимые инклуды и объявим структуру FileMapping, хранящую файловый дескриптор, размер файла и указатель на участок памяти с отображением:
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
struct FileMapping {
int fd;
size_t fsize;
unsigned char* dataPtr;
};
Рассмотрим чтение из файла с использованием отображения.
Открываем файл на чтение:
if(fd < 0) {
std::cerr << "fileMappingCreate - open failed, fname = "
<< fname << ", " << strerror(errno) << std::endl;
return nullptr;
}
Узнаем размер файла:
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 создаем отображение файла в память:
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 и возвращаем указатель на нее в качестве результата:
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
байт содержимого файла.
Как всегда, не забываем освобождать за собой ресурсы, когда они становятся ненужны:
close(mapping->fd);
free(mapping);
Вот и все! Сожалею, если вы ожидали чего-то более сложного :) Полную версию исходного кода вы найдете здесь.
Те, кому представленный материал показался слишком простым, могут в качестве домашнего задания сделать следующее:
- Взять одну из *BSD систем и проверить, работает ли код на ней;
- Переписать пример так, чтобы файл можно было не только читать, но и писать в него;
- Выяснить, можно ли менять размер файла, отображенного в память;
- Выяснить, что будет, если создать отображение файла, а затем записать в него что-то через обычный вызов write;
- Погуглить на тему использования mmap в качестве IPC, написать соответствующий пример;
Признаюсь, сам я эти упражнения не выполнял, так как соответствующая задача мне пока что не подворачивалась. Поэтому буду очень вам благодарен, если вы отпишите о результатах их решения в комментариях.
Дополнение: Обратите также внимание на системные вызовы mlock / munlock, msync, madvise и mremap. В определенных типах приложений (например, СУБД) они могут быть очень и очень полезны!
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.