12.6. Обращение к объектам, отображенным в память
Когда в память отображается обычный файл, размер полученной области (второй аргумент вызова mmap), как правило, совпадает с размером файла. Например, в листинге 12.3 размер файла устанавливается равным размеру структуры shared вызовом write и это же значение размера области используется при отображении его в память. Однако эти два параметра — размер файла и размер области памяти, в которую он отображен, — могут и отличаться.
Для детального изучения особенностей функции mmap воспользуемся программой в листинге 12.6.
//shra/test1.c
1 #include "unpipc.h"
2 int
3 main(int argc, char **argv)
4 {
5 int fd, i;
6 char *ptr;
7 size_t filesize, mmapsize, pagesize;
8 if (argc != 4)
9 err_quit("usage: test1
10 filesize = atoi(argv[2]);
11 mmapsize = atoi(argv[3]);
12 /* открытие файла, установка его размера */
13 fd = Open(argv[1], O_RDWR | O_CREAT | O_TRUNC, FILE_MODE);
14 Lseek(fd, filesize-1, SEEK_SET);
15 Write(fd, "", 1);
16 ptr = Mmap(NULL, mmapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
17 Close(fd);
18 pagesize = Sysconf(_SC_PAGESIZE);
19 printf("PAGESIZE = %ld\n", (long) pagesize);
20 for (i = 0; i < max(filesize, mmapsize); i += pagesize) {
21 printf("ptr[*d] = %d\n", i, ptr[i]);
22 ptr[i] = 1;
23 printf("ptr[%d] = %d\n", i + pagesize – 1, ptr[i + pagesize – 1]);
24 ptr[i + pagesize – 1] = 1;
25 }
26 printf("ptr[%d] = %d\n", i, ptr[i]);
27 exit(0);
28 }
8-11 Аргументы командной строки задают полное имя создаваемого и отображаемого в память файла, его размер и размер области памяти.
12-15 Если файл не существует, он будет создан. Если он существует, его длина будет установлена равной нулю. Затем размер файла устанавливается равным указанному размеру путем вызова lseek для установки текущей позиции, равной трe-буемому размеру минус 1 и записи 1 байта.
16-17 Файл отображается в память, причем размер области задается последним аргументом командной строки. Затем дескриптор файла закрывается.
18-19 Размер страницы памяти получается вызовом sysconf и выводится на экран.
20-26 Считываются и выводятся данные из области памяти, в которую отображен файл. Считываются первый и последний байты каждой страницы этой области памяти. Все значения должны быть нулевыми. Затем первый и последний байты каждой страницы устанавливаются в 1. Одно из обращений к памяти может привести к отправке сигнала процессу, что приведет к его завершению. После завершения цикла for мы добавляем еще одно обращение к следующей странице памяти, что должно заведомо привести к ошибке и завершению пpoгрaммы (если ошибка не возникла раньше).
Рассмотрим первую ситуацию: размер файла совпадает с размером области памяти, но эта величина не кратна размеру страницы памяти в данной реализации:
solaris % ls –l foo
foo: No such file or directory
solaris % test1 foo 5000 5000
PAGESIZE = 4096
ptr[0] = 0
ptr[4095] = 0
ptr[4096] = 0
ptr[8191] = 0
Segmentation Fault(coredump)
solaris % ls-l foo
-rw-r--r-- 1 rstevens other1 5000 Mar 20 17:18 foo
solaris % od –b –A d foo
0000000 001 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
0000016 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
*
0004080 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 001