В прошлый раз мы поговорили об отображении файлов в память при помощи 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 . В определенных типах приложений (например, СУБД) они могут быть очень и очень полезны!