Шрифт:
Интервал:
Закладка:
int msg_exists(int id, long type) {
int res;
if ((result = msgrcv(id, NULL, 0, type, IPC_NOWAIT)) == -1) {
if (errno == E2BIG)
return(TRUE);
}
return(FALSE);
}
В вызове msgrcv() отсутствует адрес буфера и длина сообщения. Этим мы специально провоцируем ошибку, а вызов IPC_NOWAIT отказывает от блокировки процесса. Мы проверяем errno; если он равен E2BIG, значит, сообщение есть в очереди. Ошибка E2BIG порождается потому, что мы установили размер сообщения равным 0.
26.5.6. Тотальный контроль
До сих пор мы рассматривали только системные вызовы для работы с сообщениями очереди, сейчас рассмотрим системный вызов msgctl(), предназначенный для контроля самой очереди.
int msgctl(int msgqid, int cmd, struct msqid_ds *buf);
Первый аргумент — это ID очереди, второй — команда, которую нужно выполнить:
♦ IPC_STAT — записывает в буфер buf структуру msqid_ds для очереди сообщений с идентификатором msgqid.
♦ IPC_SET — устанавливает значение ipc_perm структуры msqid. Значение берется из буфера buf.
♦ IPC_RMID — удаляет очередь.
Системный вызов возвращает 0 в случае успеха и -1, если произошла ошибка. Переменная errno устанавливается следующим образом:
♦ EACCESS — недостаточно прав.
♦ EFAULT — невозможно получить доступ к адресу буфера buf или неверный адрес.
♦ EIDRM — очередь была уничтожена прямо во время запроса.
♦ EINVAL — ошибка в аргументах, например, неправильный ID очереди или отрицательный размер сообщения.
♦ EPERM — у вас нет прав на запись в очередь.
Структура msqid_ds уже рассматривалась ранее, поэтому не вижу смысла приводить ее описание еще раз.
Если подытожить, то все, что мы можем сделать с очередью — это удалить ее и изменить права доступа или информацию о владельце (его UID и GID). Как удалить, думаю, понятно. Напишем функцию change_mode(), которая будет изменять права доступа к нашей очереди. Ей нужно передать два параметра — идентификатор очереди и новый режим доступа в виде строки, например, "0660".
int change_mode(int id, char *mode) {
struct msqid_ds buf;
/* Получаем копию структуры в буфер buf */
if (msgctl(id, IPC_STAT, &buf) == -1) {
return(-1);
}
/* Изменяем права доступа */
sscanf(mode, "%ho", &buf.msg_perm.mode);
/* Модернизируем внутреннюю структуру */
if (msgctl(id, IPC_SET, &buf) == -1} {
return(-1);
}
return(0);
}
Наша функция возвращает 0 в случае успеха или -1, если произошла ошибка.
На этом обзор средств для работы с очередями сообщений можно считать законченным, теперь с чистой совестью перейдем к следующему средству IPC — семафорам.
26.6. Семафоры
Семафор — это объект IPC, управляющий доступом к общим ресурсам (устройствам). Семафоры не позволяют одному процессу захватить устройство до тех пор, пока с этим устройством работает другой процесс. Семафор может находиться в двух положениях: 0 (устройство занято) и 1 (устройство свободно).
Одиночный семафор используется редко, практически никогда. Для контроля доступа к ресурсам обычно используются множества семафоров, даже если это множество состоит всего из одного семафора. Например, пусть у нас есть три принтера. Когда вы посылаете задание на печать, диспетчер печати просматривает множество семафоров принтеров и выясняет, есть ли свободный принтер. Если да, то он начинает печатать ваше задание, если же нет, диспетчер ставит ваше задание в очередь печати.
Еще один пример использования семафоров — это счетчики ресурсов. Представим, что вместо принтера есть некий контроллер, позволяющий выполнять 100 заданий одновременно. Когда он свободен, значение семафора равно 100. По мере поступления заданий диспетчер контроллера уменьшает значение семафора на 1, а по мере их выполнения увеличивает на 1. Когда значение достигает 0, новое задание ставится в очередь до освобождения контроллера.
Как и в случае с очередями сообщений, для семафоров в ядре Linux есть своя структура — semid_ds, которая описана в файле /usr/src/linux/include/linux/sem.h:
struct semid_ds {
struct ipc_perm sem_perm; /* права доступа */
time_t sem_otime; /* время последней операции */
time_t sem_ctime; /* время последнего изменения */
struct sem *sem_base; /* указатель на первый семафор */
struct wait_queue *eventn; /* очереди ожидания */
struct wait_queue *eventz;
struct sem_undo *undo; /* запросы undo в этом массиве */
ushort sem_nsems; /* номера семафоров в массиве */
};
Обратите внимание: в структуре есть указатель на первый семафор. Тип указателя — sem. Данный тип описывает семафор:
struct sem {
short sempid; /* pid последней операции */
ushort semval; /* текущее значение семафора */
ushort semncnt; /* число процессов, ожидающих
освобожд. рес. */
ushort semzcnt; /* число процессов, ожидающих
освоб. всех рес. */
};
♦ sem_pid
PID процесса, который произвел последнюю операцию над семафором.
♦ sem_semval
Текущее значение семафора.
♦ sem_semncnt
Число процессов, ожидающих увеличения значения семафора, то есть освобождения ресурсов.
♦ sem_semzcnt
Число процессов, ожидающих освобождения всех ресурсов.
26.6.1. Создание множества семафоров
Для создания множества семафоров или подключения к уже существующему множеству используется системный вызов semget():
int semget(key_t key, int nsems, int semflg);
Первый аргумент — это ключ IPC, который, как обычно, создается системным вызовом ftok(). Он сравнивается с ключами других семафоров и в зависимости от значения semflg решается, создавать новое множество или подключиться к уже существующему. Значение semflg:
♦ IPC_CREAT — создать новое множество семафоров;
♦ IPC_EXCL — при использовании с IPC_CREAT порождает ошибку, если семафор уже существует.
При создании семафора, как и при создании очереди сообщений, мы можем указать права доступа:
IPC_CREAT | 0660
Второй аргумент системного вызова semget() задает требуемое количество семафоров. Оно ограничено в файле sem.h:
#define SEMMSL 32 /* <= 512 */
Данный аргумент игнорируется, если вы подключаетесь к уже существующему множеству, а не создаете новое.
Функция semget() возвращает идентификатор семафора или -1 в случае ошибки. Переменная errno устанавливается следующим образом:
♦ EACCESS — у вас не хватает полномочий для выполнения операции;
♦ EEXISTS — множество существует, его нельзя создать;
♦ EIDRM — множество помечено для удаления;
♦ ENOENT — множество не существует, не было ни одной операции IPC_CREAT;
♦ ENOMEM — не хватает памяти;
♦ ENOSPC — достигнуто максимальное количество семафоров.
Функция для открытия существующего семафора может выглядеть так:
void open_sem(int *sid, key_t key) {
if ((*sid = semget(key, 0, 0666)) == -1) {
printf("Семафор не существует !!!!n");
exit(1);
}
}
Для создания множества семафоров можно использовать следующую функцию:
void create_sem(int *sid, key_t key, int n) {
int c=0; /* счетчик */
union semun sems;
if (n > SEMMSL) {
printf("Превышен лимит. Максимальное число семафоров %dn", SEMMSL);
exit(1);
}
if ((*sid =
semget(key, n, IPC_CREAT|IPC_EXCL|0666)) == -1) {
printf("Множество уже существуетn");
exit(1);
}
sems.val = SEM_RESOURCE_MAX;
/* Инициализируем все элементы одним значением */
semctl(*sid, c, SETALL, sems);
/* Если нужно установить разные значения,
нужно использовать SETVAL, например
for (c=0; c<n; c++)
semctl(*sid, c, SETVAL, sems);
*/
}
26.6.2. Выполнение операций над семафорами
Для выполнения операций над множеством семафоров служит системный вызов semop():
int semop(int semid, struct sembuf *sops, unsigned nsops);
Первый аргумент — это идентификатор семафора, возвращаемый вызовом semget(). Второй — это массив операций, которые нужно выполнить над семафорами. Последний аргумент — это количество операций в массиве.
Второй аргумент представляет собой массив типа sembuf:
struct sembuf {
ushort sem_num; /* номер семафора в массиве */
short sem_op; /* операция над семафором */
short sem_flg; /* флаги */
};
♦ sem_num
Номер семафора, над которым нужно выполнить операцию
- Операционная система UNIX - Андрей Робачевский - Программное обеспечение
- Разработка приложений в среде Linux. Второе издание - Майкл Джонсон - Программное обеспечение
- Искусство программирования для Unix - Эрик Реймонд - Программное обеспечение
- Linux - Алексей Стахнов - Программное обеспечение
- Fedora 8 Руководство пользователя - Денис Колисниченко - Программное обеспечение
- Недокументированные и малоизвестные возможности Windows XP - Роман Клименко - Программное обеспечение
- Изучаем Windows Vista. Начали! - Дмитрий Донцов - Программное обеспечение
- Windows Vista - Виталий Леонтьев - Программное обеспечение
- Архитектура операционной системы UNIX - Морис Бах - Программное обеспечение
- Windows Vista. Трюки и эффекты - Юрий Зозуля - Программное обеспечение