За флагами следует файловый дескриптор, fd
, для файла, который предстоит отобразить в памяти. Если применялся флаг MAP_ANONYMOUS
, его значение игнорируется. Последний параметр определяет, где именно в файле должно начаться отображение. Он должен быть целым числом, кратным размеру страницы. Большинство приложений начинают отображение с начала файла, указывая в качестве offset
ноль.
Системный вызов mmap()
возвращает адрес, который должен храниться в указателе. Если произошла ошибка, он возвращает адрес, эквивалентный -1
. Для проверки этого необходимо привести тип константы -1
к caddr_t
, а не к int
. Это гарантирует, что результат будет верным независимо от размеров указателей и целых чисел.
Ниже приведена программа, действующая подобно команде cat
и ожидающая отдельного имени в качестве аргумента командной строки. Она открывает этот файл, отображает его в памяти и записывает целый файл на стандартное устройство вывода одним вызовом write()
. Полезно сравнить этот пример с простой реализацией cat
из главы 11. Код примера также иллюстрирует, что карты памяти остаются на месте после закрытия отображаемого файла.
1: /* map-cat.с */
2:
3: #include
4: #include
5: #include
6: #include
7: #include
8: #include
9: #include
10:
11: int main(int argc, const char ** argv) {
12: int fd;
13: struct stat sb;
14: void * region;
15:
16: if ( fd = open(argv[1], O_RDONLY)) < 0) {
17: perror("open");
18: return 1;
19: }
20:
21: /* Вызвать fstat для файла, чтобы узнать, сколько необходимо памяти для его отображения */
22: if (fstat(fd, &sb)) {
23: perror("fstat");
24: return 1;
25: }
26:
27: /* можно было бы также отобразить как MAP_PRIVATE, поскольку
28: запись в эту память не планируется */
29: region = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
30: if (region == ((caddr_t) -1)) {
31: perror("mmap");
32: return 1;
33: }
34:
35: close(fd);
36:
37: if (write(1, region, sb.st_size) != sb.st_size) {
38: perror("write");
39: return 1;
40: }
41:
42: return 0;
43: }
13.2.3. Отмена отображения областей
После окончания отображения в памяти процесс может отменить отображение памяти с помощью munmap()
. Это приводит к тому, что последующие доступы к этому адресу будут генерировать SIGSEGV
(если только память не будет перераспределена) и сохраняет некоторые системные ресурсы. Отображение всех областей памяти отменяется, когда процесс заканчивает или начинает новую программу с помощью системного вызова exec()
.
#include
int munmap(caddr_t addr, int length);
Параметр addr
— это адрес начала области памяти для отмены отображения, а length
определяет, отображение какой части области памяти должно быть отменено. Обычно отображение каждой области отменяется отдельным вызовом munmap()
. Linux может фрагментировать карты, если отменено отображение только части области, но такой код будет непереносимым.
13.2.4. Синхронизация областей памяти на диск
Если для записи в файл используется карта памяти, модифицированные страницы памяти и файл будут в течение некоторого времени отличаться. Если процессу необходимо немедленно записать страницы на диск, для этого служит msync()
.
#include
int msync(caddr_t addr, size_t length, int flags);
Первые два параметра, addr
и length
, устанавливают область для синхронизации с диском. Параметр flags
устанавливает, каким образом должны синхронизироваться память и диск. Он состоит из одного или нескольких перечисленных ниже флагов, объединенных с помощью битового "ИЛИ".