Чтение онлайн

ЖАНРЫ

UNIX: взаимодействие процессов

Стивенс Уильям Ричард

Шрифт:

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\n", i);

14 Ftruncate(fd, i);

15 printf("ptr[%d] = %d\n", 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, и мы получим те же результаты.

12.7. Резюме

Разделяемая память представляет собой самую быстродействующую форму IPC, потому что данные из разделяемой области памяти доступны всем потокам и процессам, с ней работающим. Обычно для координации совместных действий потоков и процессов, использующих разделяемую память, требуется некоторая форма синхронизации.

В этой главе мы подробно рассмотрели свойства функции mmap и отображение обычных файлов в память, потому что это один из способов обеспечения взаимодействия между неродственными процессами. После отображения файла в память для обращения к нему больше не нужно использовать вызовы read, write и lseek — вместо этого можно напрямую работать с ячейками памяти, относящимися к той области, указатель на которую возвращает mmap. Замена явных операций с файлом на обращение к ячейкам памяти может упростить программу и в некоторых случаях увеличить быстродействие.

Если необходимо совместное использование области памяти после вызова fork, можно упростить решение этой задачи, используя неименованное отображение в память. Для этого в ядрах Berkeley при вызове mmap указывается флаг MAP_ANON, а в ядрах SVR4 производится отображение специального файла /dev/zero.

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

Поделиться с друзьями: