Указатель на массив array используется командами GETALL и SETALL для получения или установки значений всех семафоров во множестве.
Последние два члена объединения специфичны только для Linux — в других UNIX-системах вы их не найдете. Эти элементы использует ядро.
В случае успеха системный вызов semctl() возвращает натуральное число, а в случае ошибки -1. Переменная errno равна:
♦ EACCESS — не хватает полномочий;
♦ EFAULT — адрес arg ошибочен;
♦ EIDRM — множество помечено для удаления:
♦ EINVAL — неправильный аргумент semid;
♦ EPERM — у вас нет прав для выполнения команды cmd;
♦ ERANGE — значение семафора вышло за пределы допустимых значений.
Пример получения значения семафора с номером N из множества sid:
int val;
val=semctl(sid, N, GETVAL, 0);
Предположим, что нам нужно вывести состояние всех трех имеющихся принтеров:
int c, val;
for (c=0; x<3; c++) {
val=semctl(sid,c,GETVAL,0);
printf('Принтер %d: %d
',c,val);
}
А вот код инициализации всех семафоров множества semid:
union semun opts;
int c;
opts.val = 1; /* первоначальное значение семафоров */
for (c=0;c<5;c++) semctl(semid, c, SETVAL, opts);
Довольно часто возникают определенные сложности с установкой прав доступа к множеству семафоров. Рассмотрим следующий код, позволяющий установить права доступа к множеству semid. Права доступа задаются в виде строки, например, «0660».
void sem_change_mode(int semid, char *mode) {
int res;
struct semid_ds semds;
union semun opts;
/* Нужно указать нашу локальную копию структуры */
opts.buf = &semds;
if ((res = semctl(semid, 0, IPC_STAT, opts)) == -1) {
printf('Error');
exit(1);
}
printf('Старые права доступа %o
',
opts.buf->sem_perm.mode);
/* Изменяем права доступа */
sscanf(mode, '%ho', &opts.buf->sem_perm.mode);
/* Обновляем внутреннюю структуру */
semctl(sid, 0, IPC_SET, opts);
}
26.7. Разделяемые сегменты памяти
Сегменты памяти разделяются между несколькими процессами. Один процесс создает сегмент памяти, а другие в любом количестве — используют. Разделяемые сегменты памяти — это самый быстрый способ IPC.
Для каждого разделяемого сегмента памяти ядро поддерживает специальную структуру — shmid_ds, описанную в файле /usr/src/linux/include/linux/shm.h
:
struct shmid_ds {
struct ipc_perm shm_perm; /* права доступа */
int shm_segsz; /* размеры сегмента в байтах */
time_t shm_atime; /* время последней привязки */
time_t shm_dtime; /* время последней отвязки */
time_t shm_ctime; /* время последнего изменения */
unsigned short shm_cpid; /* PID создателя */
/* PID последнего процесса-пользователя сегмента */
unsigned short shm_lpid;
short shm_nattch; /* число привязок */
unsigned short shm_npages;
/* размеры сегмента (в страницах) */
/* массив указателей на $frames -> S$*/
unsigned long *shm_pages;
struct vm_area_struct *attaches;
/* дескрипторы для привязок */
};
Я немного сократил эту структуру, оставив описание только нужных нам полей. Полагаю, что вы в нем разберетесь. Возможно, вас заинтересовали термины «привязка» и «отвязка». Привязка — это размещение сегмента в адресном пространстве процесса, подключение к разделяемому сегменту памяти (РСП). Отвязка, соответственно, — отключение, Поле shm_nattch содержит количество привязок к РСП на данный момент.
Для создания нового РСП используется системный вызов shmget(). Этот же вызов используется для подключения к уже существующему РСП.
int shmget(key_t key, int size, int shmflg);
Первый аргумент — это ключ IPC, полученный с помощью ftok(), второй — размер РСП в байтах, а третий — флаги системного вызова shmget. Если установлен флаг IPC_CREAT, системный вызов создаст новый РСП или подключится к уже существующему сегменту, если обнаружится, что уже есть такой сегмент (с таким же значением ключа). Если установлен флаг IPC_EXCL вместе с IPC_CREAT (сам по себе он бесполезен) подключение к существующему РСП запрещается.
Системный вызов shmget() возвращает идентификатор РСП или -1, если произошла ошибка. Переменная errno устанавливается так:
♦ EACCESS — не хватает полномочий для доступа к сегменту;
♦ EINVAL — неправильно заданы размеры сегмента;
♦ EEXISTS — сегмент уже существует, создание невозможно. Вы получите эту ошибку, если будете использовать флаг IPC_EXCL вместе с IPC_CREAT при условии, что сегмент уже существует;
♦ IDRM — сегмент помечен на удаление или уже удален;
♦ ENOMEM — не хватает памяти для создания сегмента.
Приведем пример функции открытия/создания РСП:
int open_shms(key_t key, int size) {
return (shmget(key, size, IPC_CREAT | 0660 )) == -1));
}