Рейтинговые книги
Читем онлайн Linux: Полное руководство - Денис Колисниченко

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 138 139 140 141 142 143 144 145 146 ... 158

Второй аргумент представляет собой массив типа sembuf:

struct sembuf {

 ushort sem_num; /* номер семафора в массиве */

 short sem_op; /* операция над семафором */

 short sem_flg; /* флаги */

};

♦ sem_num

Номер семафора, над которым нужно выполнить операцию

♦ sem_op

Выполняемая операция. Может быть отрицательным или положительным числом. Если число отрицательно, значение семафора будет уменьшено, а если положительным — увеличено. Не забывайте, освобождая ресурс, увеличивать значение семафора — за вас никто это не сделает. Если sem_op = 0, то процесс «заснет» и не «проснется» до тех пор, пока значение семафора не станет 0.

♦ sem_flg

Флаги операции, например, IPC_NOWAIT. Если IPC_NOWAIT не установлен, то процесс «заснет» до тех пор, пока не освободится указанное количество ресурсов (пока другой процесс не освободит их).

Чтобы лучше понять, что такое semop(), вернемся к нашим принтерам. Пусть у нас есть всего один принтер, умеющий выполнять только одно задание за раз. Начальное значение семафора принтера будет равно 1.

Перед посылкой задания на принтер нужно убедиться, что он свободен, то есть получить от семафора значение 1. Заполним массив sembuf необходимой для выполнения операции информацией:

struct sembuf prn_lock = {0, -1, IPC_NOWAIT};

Здесь 0 — это номер семафора: у нас всего один принтер, а нумерация начинается с нуля. -1 — это операция, запрашивающая единицу ресурса. Если принтер свободен, то после выполнения этой операции значение семафора принтера будет равно 0.

Мы также установили флаг IPC_NOWAIT, чтобы вызов прошел немедленно. Если принтер занят, вызов вернет ошибку:

if (semop(sid, &prn_lock, 1) == -1)

 printf("Принтер занятn");

Первый аргумент — это идентификатор объекта IPC, второй — это массив операций. Последний аргумент semop() говорит о том, что у нас есть только одна структура типа sembuf, то есть нам нужно выполнить только одну операцию.

После выполнения задания мы должны сообщить семафору об освобождении ресурса:

struct sembuf prn_unlock ={0, 1, IPC_NOWAIT};

semop(sid, &prn_unlock, 1);

В случае успеха, когда выполнены все операции, системный вызов semop() возвращает 0. В случае ошибки возвращается -1, а errno равна:

♦ E2BIG — количество операций (аргумент nsops) превышает разрешенное число операций;

♦ EACCESS — не хватает полномочий;

♦ EAGAIN — операция не может быть выполнена (при использовании флага IPC_NOWAIT), такую операцию нужно повторить снова;

♦ EFAULT — указатель sops указывает на ошибочный адрес;

♦ EIDRM — множество помечено на удаление;

♦ EINTR — прервано сигналом;

♦ EINVAL — неверный semid;

♦ ENOMEM — не хватает памяти для создания структуры Undo-операции;

♦ ERANGE — значение семафора вышло за пределы допустимых значений.

26.6.3. Контроль семафора

Для контроля семафора используется системный вызов semctl():

int semctl(int semid, int semnum, int cmd, union semun arg);

Первый аргумент — это идентификатор семафора, второй — номер семафора во множестве семафоров (нумерация начиняется с 0). В отличие от очереди сообщений, где достаточно было указать только идентификатор очереди, при работе с семафорами нужно обязательно указывать номер конкретного семафора, над которым вы хотите выполнить операцию. Третий аргумент — это команда, которую нужно выполнить над семафором. Возможные команды представлены в таблице 26.1.

Команды управления семафорами Таблица 26.1

Команда Назначение IPC_STAT Запоминает структуру semid_ds для множества по адресу buf объединения semun (чуть позже мы подробно рассмотрим эту структуру и объединение semun) IPC_SET Устанавливает значение члена ipc_perm структуры semid_ds IPC_RMID Удаляет множество GETALL Позволяет получить значения всех семафоров. Значения возвращаются в виде массива unsigned short, на который указывает член объединения array GETNCNT Возвращает число процессов, которые ожидают ресурсы в данный момент GETPID Возвращает PID процесса, выполнившего последний вызов semop() GETVAL Возвращает значение одного семафора GETZCNT Возвращает число процессов, которые ожидают полного освобождения ресурса SETALL Устанавливает значение семафоров. Значения берутся из члена array объединения semun SETVAL Устанавливает значение конкретного семафора. Значение берется из элемента val объединения semun

Последний аргумент — это объединение (union) аргументов, которые можно использовать для управления семафором. Рассмотрим подробнее это объединение, объявленное в файле /usr/src/linux/include/linux/sem.h:

union semun {

 int val; /* значение для SETVAL */

 struct semid_ds *buf; /* буфер для IPC_STAT и IPC_SET */

 ushort *array; /* массив для GETALL и SETALL */

 struct seminfo *__buf; /* буфер для IPC_INFO */

 void *__pad;

};

Первый член этого объединения val используется для установки значения одного семафора при использовании команды SETVAL.

Член buf используется командами IPC_STAT и IPC_SET. Это копия внутренней структуры данных семафора.

Указатель на массив 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: %dn",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("Старые права доступа %on",

 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;

1 ... 138 139 140 141 142 143 144 145 146 ... 158
На этой странице вы можете бесплатно читать книгу Linux: Полное руководство - Денис Колисниченко бесплатно.
Похожие на Linux: Полное руководство - Денис Колисниченко книги

Оставить комментарий