Читаем без скачивания Операционная система UNIX - Андрей Робачевский
Шрифт:
Интервал:
Закладка:
Работа с объектами IPC System V во многом сходна. Для создания или получения доступа к объекту используются соответствующие системные вызовы get: msgget(2) для очереди сообщений, semget(2) для семафора и shmget(2) для разделяемой памяти. Все эти вызовы возвращают дескриптор объекта в случае успеха и -1 в случае неудачи. Отметим, что функции get позволяют процессу получить ссылку на объект, которой по существу является возвращаемый дескриптор, но не позволяют производить конкретные операции над объектом (помещать или получать сообщения из очереди сообщений, устанавливать семафор или записывать данные в разделяемую память. Все функции get в качестве аргументов используют ключ key и флажки создания объекта ipcflag. Остальные аргументы зависят от конкретного типа объекта. Переменная ipcflag определяет права доступа к объекту PERM, а также указывает, создается ли новый объект или требуется доступ к существующему. Последнее определяется комбинацией (или отсутствием) флажков IPC_CREAT и IPC_EXCL.
Права доступа к объекту указываются набором флажков доступа, подобно тому, как это делается для файлов:
Значение PERM (в восьмеричном виде) Аналог прав доступа для файлов Разрешено 0400 r-------- Чтение для владельца-пользователя 0200 -w------- Запись для владельца-пользователя 0040 ---r----- Чтение для владельца-группы 0020 ----w---- Запись для владельца-группы 0004 ------r-- Чтение для всех остальных 0002 -------w- Запись для всех остальныхКомбинацией флажков можно добиться различных результатов:
Значение аргумента ipcflag Результат действия функции Объект существует Объект не существует 0 Возвращает дескриптор Ошибка: отсутствие объекта (ENOENT) PERM | IPC_CREAT Возвращает дескриптор Создает объект с соответствующими PERM правами доступа PERM | IPC_CREAT Ошибка: объект уже существует (EEXIST) Создает объект с соответствующими PERM правами доступаРабота с объектами IPC System V во многом похожа на работу с файлами в UNIX. Одним из различий является то, что файловые дескрипторы имеют значимость в контексте процесса, в то время как значимость дескрипторов объектов IPC распространяется на всю систему. Так файловый дескриптор 3 одного процесса в общем случае никак не связан с дескриптором 3 другого неродственного процесса (т.е. эти дескрипторы ссылаются на различные файлы). Иначе обстоит дело с дескрипторами объектов IPC. Все процессы, использующие, скажем, одну очередь сообщений, получат одинаковые дескрипторы этого объекта.
Для каждого из объектов IPC ядро поддерживает соответствующую структуру данных, отличную для каждого типа объекта (очереди сообщений, семафора или разделяемой памяти). Общей у этих данных является структура ipc_perm описывающая права доступа к объекту, подобно тому, как это делается для файлов. Основными полями этой структуры являются:
uid Идентификатор владельца-пользователя объекта gid Идентификатор владельца-группы объекта cuid UID создателя объекта cgid GID создателя объекта mode Права доступа на чтение и запись для всех классов доступа (9 битов) key Ключ объектаПрава доступа (как и для файлов) определяют возможные операции, которые может выполнять над объектом конкретный процесс (получение доступа к существующему объекту, чтение, запись и удаление).
Заметим, что система не удаляет созданные объекты IPC даже тогда, когда ни один процесс не пользуется ими. Удаление созданных объектов является обязанностью процессов, которым для этого предоставляются соответствующие функции управления msgctl(2), semctl(2), shmctl(2). С помощью этих функций процесс может получить и установить ряд полей внутренних структур, поддерживаемых системой для объектов IPC, а также удалить созданные объекты. Безусловно, как и во многих других случаях использования объектов IPC процессы предварительно должны "договориться", какой процесс и когда удалит объект. Чаще всего, таким процессом является сервер.
Сообщения
Как уже обсуждалось, очереди сообщений являются составной частью UNIX System V, они обслуживаются операционной системой, размещаются в адресном пространстве ядра и являются разделяемым системным ресурсом. Каждая очередь сообщений имеет свой уникальный идентификатор. Процессы могут записывать и считывать сообщения из различных очередей. Процесс, пославший сообщение в очередь, может не ожидать чтения этого сообщения каким-либо другим процессом. Он может закончить свое выполнение, оставив в очереди сообщение, которое будет прочитано другим процессом позже.
Данная возможность позволяет процессам обмениваться структурированными данными, имеющими следующие атрибуты:
□ Тип сообщения (позволяет мультиплексировать сообщения в одной очереди)
□ Длина данных сообщения в байтах (может быть нулевой)
□ Собственно данные (если длина ненулевая, могут быть структурированными)
Очередь сообщений хранится в виде внутреннего однонаправленного связанного списка в адресном пространстве ядра. Для каждой очереди ядро создает заголовок очереди (msqid_ds), где содержится информация о правах доступа к очереди (msg_perm), ее текущем состоянии (msg_cbytes — число байтов и msg_qnum — число сообщений в очереди), а также указатели на первое (msg_first) и последнее (msg_last) сообщения, хранящиеся в виде связанного списка (рис. 3.18). Каждый элемент этого списка является отдельным сообщением.
Рис. 3.18. Структура очереди сообщений
Для создания новой очереди сообщений или для доступа к существующей используется системный вызов msgget(2):
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflag);
Функция возвращает дескриптор объекта-очереди, либо -1 в случае ошибки. Подобно файловому дескриптору, этот идентификатор используется процессом для работы с очередью сообщений. В частности, процесс может:
□ Помещать в очередь сообщения с помощью функции msgsnd(2);
□ Получать сообщения определенного типа из очереди с помощью функции msgrcv(2);
□ Управлять сообщениями с помощью функции msgctl(2).
Перечисленные системные вызовы манипулирования сообщениями имеют следующий вид:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp,
size_t msgsz, int msgflg);
int msgrcv(int msqid, void *msgp,
size_t msgsz, long msgtyp, int msgflg);
Здесь msgid является дескриптором объекта, полученного в результате вызова msgget(2). Параметр msgtyp указывает на буфер, содержащий тип сообщения и его данные, размер которого равен msgsz байт. Буфер имеет следующие поля:
long msgtype тип сообщения char msgtext[] данные сообщенияАргумент msgtyp указывает на тип сообщения и используется для их выборочного получения. Если msgtyp равен 0, функция msgrcv(2) получит первое сообщение из очереди. Если величина msgtyp выше 0, будет получено первое сообщение указанного типа. Если msgtyp меньше 0, функция msgrcv(2) получит сообщение с минимальным значением типа, меньше или равного абсолютному значению msgtyp.