linux-file-mapping/

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

EnglishRussianUkrainian