12.6. Обращение к объектам, отображенным в память

We use cookies. Read the Privacy and Cookie Policy

12.6. Обращение к объектам, отображенным в память

Когда в память отображается обычный файл, размер полученной области (второй аргумент вызова mmap), как правило, совпадает с размером файла. Например, в листинге 12.3 размер файла устанавливается равным размеру структуры shared вызовом write и это же значение размера области используется при отображении его в память. Однако эти два параметра — размер файла и размер области памяти, в которую он отображен, — могут и отличаться.

Для детального изучения особенностей функции mmap воспользуемся программой в листинге 12.6.

Листинг 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 <pathname> <filesize> <mmapsize>");

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 ", (long) pagesize);

20  for (i = 0; i < max(filesize, mmapsize); i += pagesize) {

21   printf("ptr[*d] = %d ", i, ptr[i]);

22   ptr[i] = 1;

23   printf("ptr[%d] = %d ", i + pagesize – 1, ptr[i + pagesize – 1]);

24   ptr[i + pagesize – 1] = 1;

25  }

26  printf("ptr[%d] = %d ", 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

0004096 001 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000

0004112 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000

0005000

Размер страницы памяти составляет 4096 байт, и мы смогли обратиться ко всему содержимому второй страницы (индексы 4096-8191), но обращение к третьей странице (8192) приводит к отправке сигнала SIGSEGV, о чем интерпретатор оповещает сообщением Segmentation Fault. Хотя мы и установили значение ptr[8191] = 1, оно не было записано в файл и его размер остался равным 5000 байт. Ядро позволяет считывать и записывать данные в ту часть последней страницы, которая не относится к отображенному файлу (поскольку защита памяти осуществляется ядром постранично), но изменения в этой области памяти не будут скопированы в файл. А вот относящиеся к файлу изменения (индексы 0, 4095 и 4096) были скопированы в него, в чем мы убедились, воспользовавшись программой od (параметр –b при вызове последней указывает на необходимость выводить значения байтов в восьмеричном формате, а параметр –Ad позволяет выводить адреса в десятичном формате). На рис. 12.8 изображена схема памяти для данного примера. 

Рис. 12.8. Размер отображаемого файла совпадает с размером области памяти

Запустив этот пример в Digital Unix 4.0B, получим тот же результат, но размер страницы памяти в этой системе равняется 8192 байт:

alpha % ls –l foo

foo not found

alpha % test1 foo5000 5000

PAGESIZE = 8192

ptr[0] = 0

ptr[8191] = 0

Memory fault (coredump)

alpha % ls -l foo

-rw-r-r– 1 rstevens operator 5000 Mar 21 08:40 foo

Мы все так же можем обратиться к памяти за пределами отображенного файла, но не выходя за грaницы страницы памяти (индексы с 5000 по 8191). Обращение к ptr[8192] приводит к отправке SIGSEGV, на что мы и рассчитывали.

Вторая ситуация: размер области памяти (15000 байт) превышает размер файла (5000 байт):

solaris % rm foo

solaris % test1 foo 5000 15000

ptr[0] = 0

ptr[4095] = 0

ptr[4096] = 0

ptr[8191] = 0

Bus Error(coredump)

solaris % ls –l foo

-rw-r-r– 1 rstevens other1 5000 Mar 20 17:37 foo 

Рис. 12.9. Размер области памяти больше размера отображаемого файла

Полученный результат аналогичен результату предыдущего примера, в котором размер файла равнялся размеру области отображения (5000 байт). Однако в данном примере генерируется сигнал SIGBUS (о чем интерпретатор оповещает сообщением Bus Error), тогда как в предыдущем примере отправлялся сигнал SIGSEGV. Отличие в том, что SIGBUS означает выход за грaницы отображенного файла внутри области отображения, a SIGSEGV — выход за грaницы области. Этим примером мы показали, что ядро хранит информацию о размере отображенного объекта, даже несмотря на то, что его дескриптор закрыт. Ядро позволяет указать при вызове mmap размер области памяти, больший размера файла, но не позволяет обратиться к адресам в этой области (кроме остатка последней страницы, в которой еще имеется содержимое собственно файла — индексы с 5000 по 8191 в данном случае). На рис. 12.9 приведена иллюстрация к этому примеру. 

Следующая программа приведена в листинге 12.7. Ею мы иллюстрируем типичные методы работы с увеличивающимися в размерах файлами: при отображении в память заказывается большой размер области, текущий размер файла учитывается при всех операциях (чтобы не выйти за его пределы в памяти), а затем он просто увеличивается, по мере того как в файл записываются данные.

Листинг 12.7. Отображение увеличивающегося файла в память

//shm/test2.c

1  #include "unpipc.h"

2  #define FILE "test.data"

3  #define SIZE 32768

4  int

5  main(int argc, char **argv)

6  {

7   int fd, i;

8   char *ptr;

9   /* открытие, создание, урезание и установка размера файла, вызов mmap */

10  fd = Open(FILE, O_RDWR | O_CREAT | O_TRUNC, FILE_MODE);

11  ptr = Mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

12  for (i = 4096; i <= SIZE; i += 4096) {

13   printf("setting file size to %d ", i);

14   Ftruncate(fd, i);

15   printf("ptr[%d] = %d ", i-1, ptr[i-1]);

16  }

17  exit(0);

18 }

Открытие файла

9-11 Мы создаем файл, если он еще не существует, или урезаем его до нулевой длины, если он существует. Затем файл отображается в область объемом 32 768 байт, хотя его текущий размер равен нулю.

Увеличение размера файла

12-16 Мы увеличиваем размер файла на 4096 байт за один вызов ftruncate (раздел 13.3) и считываем из него последний байт в каждом проходе цикла.

Запустив эту программу, мы убедимся в возможности обращаться к новым данным по мере роста файла:

alpha % ls –l test.data

test.data: No such file or directory

alpha % test2

setting file size to 4096

ptr[4095] = 0

setting file size to 8192

ptr[8191] = 0

setting file size to 12288

ptr[12287] = 0

setting file size to 16384

ptr[16383] = 0

setting file size to 20480

ptr[20479] = 0

setting file size to 24576

ptr[24575] = 0

setting file size to 28672

ptr[28671] = 0

setting file size to 32768

ptr[32767] = 0

alpha % ls-l test.data

-rw-r--r-- 1 rstevens other1 32768 Mar 20 17:53 test.data

Этот пример показывает, что ядро всегда следит за размером отображаемого в память объекта (в данном примере это файл test.data), и мы всегда имеем возможность обратиться к байтам, лежащим внутри области, ограниченной размером файла и размером отображения. Те же результаты получаются при запуске этой программы в Solaris 2.6.

В этом разделе мы работали с отображением файлов в память с помощью mmap. В упражнении 13.1 нам придется немного изменить две наших программы для работы с разделяемой памятью Posix, и мы получим те же результаты.

Данный текст является ознакомительным фрагментом.