Объекты в памяти
Отображение объектов в адресное пространство процессов
Идея, лежащая в основе рассматриваемого класса средств, в сущности, весьма проста. Если отобразить объект в адресное пространство процесса, то доступ к объекту можно осуществлять обычными операциями чтения/записи. Если один объект отображен в адресное пространство нескольких процессов, он превращается в средство межпроцессного взаимодействия, так как данные, записанные в объект одним процессом, появляются в адресных пространствах всех участников отображения.
Отображаться могут обычные файлы, а также объекты в разделяемой и типизированной памяти.
Реализация должна обеспечивать, чтобы отображению подвергалось целое число страниц памяти из адресного пространства процесса. Размер страницы является значением конфигурационной переменной PAGESIZE. Отображаемая часть объекта должна начинаться с границы страницы. Длина отображаемой части не обязана быть кратной странице.
Попытки доступа к отображенной памяти, лежащей за текущей границей объекта, приводят к генерации сигнала SIGBUS.
К отображенным страницам могут предоставляться различные виды доступа: на чтение, запись и/или выполнение. При попытке нарушить наложенные ограничения процессу доставляется сигнал SIGSEGV.
Отображение объектов в адресное пространство процессов осуществляется функцией mmap() (см. листинг 5.1).
#include <sys/mman.h>
void *mmap (void *addr, size_t len,
int prot, int flags,
int fildes, off_t off);
Листинг
5.1.
Описание функции mmap().
Отображаемая часть объекта задается файловым дескриптором fildes, смещением off и длиной len. (Вот для чего, заметим в скобках, при открытии объекта в разделяемой памяти функцией shm_open() возвращается файловый дескриптор - чтобы передать его mmap() и отобразить объект в адресное пространство процесса, а вовсе не для того, чтобы применять к нему операции файлового ввода/вывода. В исторически сложившихся реализациях функция mmap() появилась как средство отображения в память обычных файлов, так что с расширением функциональности другие виды отображаемых объектов пришлось стричь под ту же гребенку.)
Значения аргументов addr, prot и flags задают характеристики отображения - рекомендуемый адрес в адресном пространстве процесса, запрашиваемые виды доступа к отображенным страницам и управляющие флаги.
Результатом функции mmap() служит выбранный реализацией адрес в адресном пространстве процесса, начиная с которого отображен заданный фрагмент объекта, или, в случае неудачи, значение MAP_FAILED. Если в аргументе flags установлен флаг MAP_FIXED, результирующий адрес должен совпадать со значением addr ; предполагается, что приложение располагает знаниями о целевой архитектуре, достаточными для точной спецификации отображения, что, впрочем, отрицательным образом сказывается на мобильности. Если флаг MAP_FIXED не установлен, а значение addr равно NULL, реализация имеет полную свободу в выборе результирующего адреса; подобную комбинацию следует рекомендовать как максимально мобильную.
В значении аргумента flags, помимо MAP_FIXED, могут быть установлены флаги MAP_SHARED (запись в отображенную память изменяет отображаемый объект со всеми вытекающими отсюда последствиями) или MAP_PRIVATE (изменения в отображенной памяти видны только записывающему процессу и не распространяются на отображаемый объект). Стандарт POSIX-2001 не специфицирует, имеет ли место обратная зависимость, то есть видны ли процессу, установившему флаг MAP_PRIVATE, изменения в отображаемом объекте.
Аргумент prot, определяющий разрешенные виды доступа к отображенным страницам, строится как комбинация флагов PROT_READ, PROT_WRITE и PROT_EXEC (разрешен доступ, соответственно, на чтение, запись и/или выполнение ). Альтернативный вариант - установить флаг PROT_NONE, запрещающий доступ к странице (из общих соображений выключатель всегда должен быть под рукой). Значение аргумента prot должно быть согласовано с флагами, указанными при открытии объекта и получении дескриптора fildes.
Стандарт POSIX-2001 предусматривает возможность динамической смены разрешенных видов доступа к отображенным страницам посредством вызова функции mprotect() (см. листинг 5.2).
#include <sys/mman.h>
int mprotect (void *addr,
size_t len, int prot);
Листинг
5.2.
Описание функции mprotect().
Виды доступа меняются в соответствии со значением аргумента prot для всех страниц, пересекающихся с частью адресного пространства процесса, начинающейся со значения addr (которое должно указывать на границу страницы) и имеющей длину len байт.
Нормальным результатом функции mprotect() (и описываемых далее функций) служит нуль; в случае ошибки возвращается -1.
Для отмены отображений в адресное пространство процессов служит функция munmap() (см. листинг 5.3).
#include <sys/mman.h> int munmap (void *addr, size_t len);Листинг 5.3. Описание функции munmap().
Отображения отменяются для страниц, пересекающихся с частью адресного пространства процесса, начинающейся со значения addr (которое должно указывать на границу страницы) и имеющей длину len байт.
Отображение объектов в адресное пространство процессов - универсальное, исключительно мощное средств. В качестве примера применим его для реализации упрощенного аналога служебной программы cat (см. листинг 5.4).
/* * * * * * * * * * * * * * * * * * * * * * * */
/* Программа выдает на стандартный вывод файлы,*/
/* имена которых заданы в командной строке */
/* * * * * * * * * * * * * * * * * * * * * * * */
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
int main (int argc, char *argv []) {
struct stat sbuf; /* Структура для получения информации */
/* о файле */
int fd; /* Дескриптор обрабатываемого файла */
void *maddr; /* Адрес отображенного в память файла */
int i;
for (i = 1; i < argc; i++) {
if ((fd = open (argv [i], O_RDONLY)) < 0) {
perror ("OPEN");
continue;
}
if (fstat (fd, &sbuf) != 0) {
perror ("FSTAT");
close (fd);
continue;
}
if ((maddr = mmap (NULL, sbuf.st_size,
PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED){
perror ("MMAP");
close (fd);
continue;
}
if (write (STDOUT_FILENO, maddr,
sbuf.st_size) != sbuf.st_size) {
perror ("WRITE");
}
if (munmap (maddr, sbuf.st_size) != 0) {
perror ("MUNMAP");
}
close (fd);
} /* for */
return 0;
}
Листинг
5.4.
Пример программы, использующей файлы, отображенные в память.
Отметим, что обрабатываемые файлы только открываются, но не читаются, а отображаются в память, откуда и выдаются на стандартный вывод. Роль буфера играют отображенные страницы.