Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Системное программирование в среде Linux. Учебное пособие

.pdf
Скачиваний:
2
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
☆
value = 2 // значение после возвращения из sem_post [gun@CentOS]$ ./semgetvalue /test1 value = 2
Именованные семафоры POSIX могут быть размещены не только в виртуальной файловой системе, но и в отображаемом на память файле. Отображаемые в память файлы описаны в следующем разделе.
По окончании работы с примерами не забудем удалить семафор:
[gun@CentOS]$ ./semunlink /test1 Result:Success
Убедимся визуально, что семафор удален:
[gun@CentOS]$ ls -l /dev/shm/sem.test1 ls: невозможно получить доступ
к
/dev/shm/sem.test1: Нет такого файла или каталога
Функции sem_init и sem_destroy
До сих пор обсуждались только именованные семафоры POSIX. Они идентифицируются аргументом name, обычно представляющим собой имя файла в файловой системе. Стандарт POSIX описывает так­же семафоры, размещаемые в памяти, память под которые (типа sem_t) выделяет приложение, а инициализируются они системным вызовом:
#include <semaphore.h> int sem_init(sem_t *sem, int shared, unsigned int value );
Функция возвращает 0
в случае успешного завершения или –1
в случае ошибки.
Размещаемый в памяти семафор инициализируется вызовом sem_init. Аргумент sem указывает на переменную типа sem_t, ме­сто под которую должно быть выделено приложением. Если аргумент
shared равен 0, то семафор используется потоками одного процесса (см. главу 7), в противном случае доступ к нему могут иметь
несколько процессов. Если аргумент shared ненулевой, семафор должен быть размещен в одном из видов разделяемой памяти и должен быть досту­пен всем процессам, использующим его. Как и в вызове sem_open, аргумент value задает начальное значение семафора.
151
Обратите внимание, что для размещаемого в памяти семафора нет ничего аналогичного флагу O_CREAT: функция sem_init всегда инициализирует значение семафора. Следовательно, нужно быть вни­мательным, чтобы вызывать sem_init только один раз для каждого семафора. При вызове sem_init для уже инициализированного се­мафора результат непредсказуем.
После завершения работы с размещаемым в памяти семафором
его
можно уничтожить, вызвав sem_destroy:
int sem_destroy(sem_t *sem );
Функция возвращает 0 в случае успешного завершения или –1 в случае ошибки.
Размещаемый в памяти семафор может быть использован в тех случаях, когда нет необходимости использовать имя, связываемое с именованным семафором. Именованные семафоры обычно использу­ются для синхронизации работы неродственных процессов. Имя в этом случае используется для
идентификации семафора.
Размещаемый в памяти семафор не утрачивает функциональности до тех пор, пока память, в которой он размещен, еще доступна какому­либо процессу.
Если размещаемый в памяти семафор совместно используется по­токами одного процесса (аргумент shared при вызове sem_init ра­вен 0), семафор обладает живучестью процесса и удаляется при завер­шении
последнего.
Если размещаемый в памяти семафор совместно используется не­сколькими процессами (аргумент shared при вызове sem_init ра­вен 1), он должен располагаться в разделяемой памяти, и в этом случае семафор существует столько, сколько существует эта область памяти, поскольку и разделяемая память POSIX, и разделяемая память System V обладают живучестью ядра.
Ограничения на семафоры
Стандартом POSIX
определены два ограничения на семафоры:
SEM_NSEMS_MAX – максимальное количество одновременно от­крытых семафоров для одного процесса (POSIX требует, чтобы это значение было не менее 256);
SEM_VALUE_MAX – максимальное значение семафора (POSIX требует, чтобы оно было не меньше 32 767).
152
Две эти константы обычно определены в заголовочном файле <unistd.h> и могут быть получены во время выполнения вызовом sys-
conf, как показано в файле semsysconf.c, доступном на сайте [6].
При запуске этой программы в системе CentOS 6.9 получим следу­ющий результат:
[gun@CentOS]$ ./semsysconf SEM_NSEMS_MAX = 256, SEM_VALUE_MAX = 32767
6.4.4. Разделяемая память POSIX IPC
Стандарт POSIX.1 предоставляет два механизма совместного ис­пользования областей памяти для неродственных процессов:
1. Отображение файлов в память: файл открывается вызовом open, а его дескриптор используется при вызове функции mmap для отобра­жения содержимого файла в адресное пространство процесса. Этот метод позволяет реализовать совместное использование памяти как для родственных, так и для
2. Объекты разделяемой памяти: функция shm_open открывает объ­ект IPC с именем стандарта POSIX (например, полным именем объекта файловой системы), возвращая дескриптор, который может быть исполь­зован для отображения в адресное пространство процесса вызовом mmap.
Оба метода требуют вызова mmap. Отличие состоит в методе полу­чения дескриптора, являющегося аргументом mmap: в он возвращается функцией open, а во втором – shm_open. Это иллю­стрирует рис. 6.19. Стандарт POSIX называет объектами памяти (memory objects) и отображенные в память файлы, и объекты разделя­емой памяти стандарта POSIX.
неродственных процессов.
первом случае
Рис. 6.19. Объекты памяти POSIX: отображаемые в память
файлы и объекты разделяемой памяти
153
Поскольку оба подхода требуют использования функций для рабо­ты с отображаемыми на память файлами, вначале изучим их.
Функции mmap, munmap и msync
Функция mmap отображает в адресное пространство процесса файл или объект разделяемой памяти POSIX. Мы используем эту функцию в следующих ситуациях:
1. С обычными файлами для обеспечения ввода-вывода через отоб-
ражение в память.
2. Со специальными файлами для обеспечения неименованного отображения памяти (несуществующие файлы, как в Windows API и файл /dev/zero), но такие файлы поддерживаются только в ОС се­мейства BSD.
3. С функцией shm_open для создания участка разделяемой не­родственными процессами памяти POSIX.
#include <sys/mman.h> void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset );
Функция возвращает начальный адрес участка памяти в случае
успешного завершения или MAP_FAILED в
случае ошибки.
Аргумент addr может указывать начальный адрес участка памяти процесса, в который следует отобразить содержимое дескриптора fd. Обычно ему присваивается значение нулевого указателя, что говорит ядру о необходимости выбрать начальный адрес самостоятельно. В любом случае функция возвращает начальный адрес сегмента памяти, выделенной для отображения.
Аргумент len задает длину отображаемого участка
в байтах; уча­сток может начинаться не с начала файла, а с некоторого места, зада­ваемого аргументом offset. Обычно offset = 0. На рис. 6.20 изоб­ражена схема отображения объекта в память.
Защита участка памяти с отображенным объектом обеспечивается с помощью аргумента prot и констант, приведенных в табл. 6.13. Обычное значение этого аргумента – PROT_READ | PROT_WRITE, что обеспечивает доступ на чтение и запись.
154
Рис. 6.20. Пример отображения файла в память
Таблица 6.13
Аргумент prot для вызова mmap
prot PROT_READ PROT_WRITE PROT_EXEC PROT_NONE
Данные могут быть считаны Данные могут быть записаны Данные могут быть выполнены Доступ к данным закрыт
Описание
Аргумент flags может принимать значения из табл. 6.14. Можно указать только один из флагов – MAP_SHARED или MAP_PRIVATE, прибавив к нему при необходимости MAP_FIXED. Если указан флаг MAP_PRIVATE, все изменения будут производиться только с образом объекта в адресном пространстве процесса, другим процессам они до­ступны не будут. Если же указан флаг MAP_SHARED, изменения
отоб­ражаемых данных видны всем процессам, совместно использующим объект.
Для обеспечения переносимости пpoгрaмм флаг MAP_FIXED ука­зывать не следует. Если он не указан, но аргумент addr представляет собой ненулевой указатель, интерпретация этого аргумента зависит от реализации. Ненулевое значение addr обычно трактуется как указа­тель на желаемую область памяти,
в которую нужно произвести отоб­ражение. В переносимой программе значение addr должно быть ну­левым и флаг MAP_FIXED не должен быть указан.
155
Аргумент flag для вызова mmap
Таблица 6.14
flag
MAP SHARED
MAP_PRIVATE
MAP_FIXED
Изменения передаются другим процессам Изменения не передаются другим процессам и не вли-
яют на отображенный объект Аргумент addr интерпретируется как адрес памяти
Описание
Одним из способов добиться совместного использования памяти родительским и дочерним процессами является вызов mmap с флагом MAP_SHARED перед вызовом fork. Стандарт POSIX.1 гарантирует в этом случае, что все отображения памяти, установленные родитель­ским процессом, будут унаследованы дочерним. Более того, изменения в содержимом объекта, вносимые родительским процессом, будут видны дочернему процессу, и
наоборот.
Для отключения отображения объекта в адресное пространство процесса используется вызов munmap:
#include <sys/mman.h> int munmap(void *addr, size_t len );
Функция возвращает 0 в случае успешного завершения или –1 в случае ошибки.
Аргумент addr должен содержать адрес, возвращенный mmap, a len – длину области отображения. После вызова munmap любые по­пытки обратиться к этой области памяти приведут к отправке процессу сигнала SIGSEGV
(предполагается, что эта область памяти не будет
снова отображена вызовом mmap).
Если область была отображена с флагом MAP_PRIVATE, все вне­сенные за время работы процесса изменения сбрасываются.
В изображенной на рис. 6.20 схеме ядро обеспечивает синхрониза­цию содержимого файла, отображенного в память, с самой памятью при помощи алгоритма работы с виртуальной
памятью (если сегмент был отображен с флагом MAP_SHARED). Если происходит изменение содержимого ячейки памяти, в которую отображен файл, через неко­торое время содержимое файла будет соответствующим образом изме­нено ядром. Однако в некоторых случаях необходимо, чтобы содер-
156
жимое файла всегда было в соответствии с содержимым памяти. Тогда для осуществления моментальной синхронизации вызывают функцию
msync:
#include <sys/mman.h> int msync(void *addr, size_t len , int flags );
Функция возвращает 0 в случае успешного завершения или –1 в
случае ошибки.
Аргумент flags представляет собой комбинацию констант из
табл. 6.15.
Таблица 6.15
Значения аргумента flags для функции msync
Константа Описание
MS_ASYNC
MS_SYNC
MS_INVALIDATE
Осуществлять асинхронную запись Осуществлять синхронную запись Сбросить кэш
Из двух констант MS_ASYNC и MS_SYNC указать нужно одну и только одну. Отличие между ними в том, что возврат из функции при указании флага MS_ASYNC происходит сразу же, как только данные для записи будут помещены в очередь ядром, а при указании флага MS_SYNC возврат происходит только после завершения операций за­писи. Если указан и флаг MS_INVALIDATE, все копии файла, содер­жимое которых не совпадает с его текущим содержимым, считаются устаревшими. Последующие обращения к этим копиям приведут к считыванию данных из файла.
Удобство работы с отображением в память содержимого файла со­стоит в том, что все операции ввода-вывода осуществляются ядром и скрыты от программиста, а он просто пишет код, считывающий и за­писывающий данные в некоторую область памяти. Ему не приходится вызывать read, write или lseek. Часто это заметно упрощает код.
Следует, однако, иметь в виду, что не все файлы могут быть отоб­ражены в память. Попытка отобразить дескриптор, указывающий на терминал или сокет, приведет к возвращению ошибки при вызове mmap. К дескрипторам этих типов доступ осуществляется только с по­мощью read и write (и аналогичных вызовов).
157
Другой целью использования mmap может являться разделение памяти между неродственными процессами. В этом случае содер­жимое файла становится начальным содержимым разделяемой па­мяти и любые изменения, вносимые в нее процессами, копируются обратно в файл (что дает этому виду IPC живучесть файловой си­стемы). Предполагается, что при вызове mmap указывается флаг
MAP_SHARED, необходимый
для разделения памяти между процес-
сами.
Функции shm_open и shm_unlink
Процесс получения доступа к объекту разделяемой памяти POSIX выполняется в два этапа:
1. Вызов shm_open с именем IPC в качестве аргумента позволяет либо создать новый объект разделяемой памяти, либо открыть суще­ствующий.
2. Вызов mmap позволяет отобразить разделяемую память в адрес­ное пространство вызвавшего процесса.
Аргумент name, указанный при первом вызове shm_open, должен впоследствии использоваться всеми прочими процессами, желающими получить доступ к данной области памяти.
Причина, почему этот процесс выполняется в два этапа вместо од­ного, на котором в ответ на имя объекта возвращался бы адрес соот­ветствующей области памяти, заключается в том, что функция mmap уже существовала, когда эта форма разделяемой памяти была включе­на в стандарт POSIX.
#include <sys/mman.h> int shm_open(const char *name, int oflag, mode_t mode );
Функция возвращает неотрицательный дескриптор в случае успешного завершения или –1 в случае ошибки.
int shm_unlink(const char *name );
Функция возвращает 0 в случае успешного завершения или –1 в случае ошибки.
Аргумент oflag должен содержать флаг O_RDONLY либо O_RDWR и один из следующих: O_CREAT, O_EXCL, O_TRUNC
. Флаги O_CREAT
и O_EXCL были описаны ранее. Если вместе с флагом O_RDWR указан
158
флаг O_TRUNC, существующий объект разделяемой памяти будет уко­рочен до нулевой длины.
Аргумент mode задает биты разрешений доступа (табл. 6.11) и ис­пользуется только при указании флага O_CREAT. Обратите внимание, что в отличие от функций mq_open и sem_open для shm_open ар­гумент mode указывается всегда. Если флаг O_CREAT не указан, зна­чение
аргумента mode может быть нулевым.
Возвращаемое значение shm_open представляет собой целочис­ленный дескриптор, который может использоваться при вызове mmap в качестве пятого аргумента.
Функция shm_unlink удаляет имя объекта разделяемой памяти. Как и другие подобные функции (удаление файла из файловой систе­мы, удаление очереди сообщений и именованного семафора POSIX), она не выполняет
никаких действий до тех пор, пока объект не будет закрыт всеми открывшими его процессами. Однако после вызова shm_unlink последующие вызовы open, mq_open и sem_open выполняться не будут.
Функции ftruncate и fstat
Размер файла или объекта разделяемой памяти можно изменить
вызовом ftruncate:
#include <unistd.h> int ftruncate(int fd, off_t length );
Функция возвращает 0 в случае успешного завершения или –1
в случае ошибки.
Стандарт
POSIX делает некоторые различия в определении дей­ствия этой функции для обычных файлов и для объектов разделяемой памяти.
1. Для обычного файла: если размер файла превышает значение length, избыточные данные отбрасываются. Если размер файла оказывается меньше значения length, действие функции не опре­делено. Поэтому для переносимости следует использовать следую­щий способ увеличения
длины обычного файла: вызов seek со сдвигом length-1 и запись одного байта в файл. К счастью, почти все реализации UNIX поддерживают увеличение размера файла вы­зовом ftruncate.
159
2. Для объекта разделяемой памяти ftruncate устанавливает
размер объекта равным значению аргумента length.
Итак, ftruncate вызывается для установки размера только что созданного объекта разделяемой памяти или изменения размера суще­ствующего объекта. При открытии существующего объекта разделяе­мой памяти следует воспользоваться fstat для получения информа­ции о нем:
#include <sys/types.h> #include <sys/stat.h>
int fstat(int fd, struct stat *buf );
Функция возвращает 0 в
случае успешного завершения или –1
в случае ошибки.
В структуре stat содержится больше десятка полей, но только че­тыре из них содержат актуальную информацию, если fd представляет собой дескриптор области разделяемой памяти:
struct stat { … mode_t st_mode; /* mode: S_I{RW}{USR,GRP,OTH} */ uid_t st_uid; /* UID владельца */ gid_t st_gid; /* GID владельца */ off_t st_size; /* размер в байтах */ … };
Пример использования этих двух функций приведен ниже.
Простые программы
Приведем несколько
примеров программ, работающих с разделяе-
мой памятью POSIX.
Программа shmcreate.c, текст которой доступен на сайте [6] в раз­деле «PosixShm», создает объект разделяемой памяти с указанным именем и длиной. Вызов shm_open создает объект разделяемой памя­ти. Когда указан параметр –е, будет возвращена ошибка в том случае, если такой объект уже существует. Вызов ftruncate устанавливает длину (размер объекта), a mmap отображает его содержимое в адресное пространство процесса. Затем программа завершает работу. Поскольку
160
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]