UNIX: взаимодействие процессов - Уильям Стивенс
Шрифт:
Интервал:
Закладка:
9 return(-1);
10 }
11 if ((n = pthread_mutex_lock(&sem->sem_mutex)) != 0) {
12 errno = n;
13 return(-1);
14 }
15 while (sem->sem_count == 0)
16 pthread_cond_wait(&sem->sem_cond, &sem->sem_mutex);
17 sem->sem_count--;
18 pthread_mutex_unlock(&sem->sem_mutex);
19 return(0);
20 }
11-18 Прежде чем работать с семафором, нужно заблокировать соответствующее взаимное исключение. Если значение семафора 0, выполнение процесса приостанавливается в вызове pthread_cond_wait до тех пор, пока другой процесс не вызовет pthread_cond_signal для этого семафора, изменив его значение с 0 на 1. После того как значение становится ненулевым, мы уменьшаем его на 1 и разблокируем взаимное исключение.
Функция sem_trywait
В листинге 10.34 приведен текст функции sem_trywait, которая представляет собой просто неблокируемый вариант функции sem_wait.
11-22 Мы блокируем взаимное исключение и проверяем значение семафора. Если оно положительно, мы вычитаем из него 1 и возвращаем вызвавшему процессу код 0. В противном случае возвращается –1, а переменной errno присваивается код ошибки EAGAIN.
Листинг 10.34. Функция sem_trywait//my_pxsem_nmap/sem_trywait.с
1 #include "unpipc.h"
2 #include "semaphore.h"
3 int
4 mysem_trywait(mysem_t *sem)
5 {
6 int n, rc;
7 if (sem->sem_magic != SEM_MAGIC) {
8 errno = EINVAL;
9 return(-1);
10 }
11 if ((n = pthread_mutex_lock(&sem->sem_mutex)) != 0) {
12 errno = n;
13 return(-1);
14 }
15 if (sem->sem_count > 0) {
16 sem->sem_count--;
17 rc = 0;
18 } else {
19 rc = –1;
20 errno = EAGAIN;
21 }
22 pthread_mutex_unlock(&sem->sem_mutex);
23 return(rc);
24 }
Функция sem_getvalue
В листинге 10.35 приведен текст последней функции в этой реализации — sem_getvalue. Она возвращает текущее значение семафора.
11-16 Мы блокируем соответствующее взаимное исключение и считываем значение семафора.
Листинг 10.35. Функция sem_getvalue//my_pxsem_mmap/sem_getvalue.c
1 #include "unpipc.h"
2 #include "semaphore.h"
3 int
4 mysem_getvalue(mysem_t *sem, int *pvalue)
5 {
6 int n;
7 if (sem->sem_magic != SEM_MAGIC) {
8 errno = EINVAL;
9 return(-1);
10 }
11 if ((n = pthread_mutex_lock(&sem->sem_mutex)) != 0) {
12 errno = n;
13 return(-1);
14 }
15 *pvalue = sem->sem_count;
16 pthread_mutex_unlock(&sem->sem_mutex);
17 return(0);
18 }
Из этой реализации видно, что семафорами пользоваться проще, чем взаимными исключениями и условными переменными.
10.16. Реализация с использованием семафоров System V
Приведем еще один пример реализации именованных семафоров Posix — на этот раз с использованием семафоров System V. Поскольку семафоры System V появились раньше, чем семафоры Posix, эта реализация позволяет использовать последние в системах, где их поддержка не предусмотрена производителем.
ПРИМЕЧАНИЕ
Семафоры System V описаны в главе 11. Этот раздел можно пропустить при первом чтении, с тем чтобы вернуться к нему по прочтении 11 главы.
Начнем, как обычно, с заголовочного файла semaphore.h (листинг 10.36), который определяет фундаментальный тип данных sem_t.
Листинг 10.36. Заголовочный файл semaphore.h//my_pxsem_svsem/semaphore.h
1 /* фундаментальный тип данных */
2 typedef struct {
3 int sem_semid; /* идентификатор семафора System V */
4 int sem_magic; /* магическое значение, если семафор открыт */
5 } mysem_t;
6 #define SEM_MAGIC 0x45678923
7 #ifdef SEM_FAILED
8 #undef SEM_FAILED
9 #define SEM_FAILED ((mysem_t *)(-1)) /* исключаем предупреждения компилятора */
10 #endif
11 #ifndef SEMVMX
12 #define SEMVMX 32767 /* исторически сложившееся максимальное значение для семафора System V */
13 #endif
Тип данных sem_t1-5 Мы реализуем именованный семафор Posix с помощью набора семафоров System V, состоящего из одного элемента. Структура данных семафора содержит идентификатор семафора System V и магическое число (обсуждавшееся в связи с листингом 10.21).
Функция sem_open
В листинге 10.37 приведен текст первой половины функции sem_open, которая создает новый семафор или открывает существующий.
Листинг 10.37. Функция sem_open: первая часть//my_pxsem_svsem/sem_open. с
1 #include "unpipc.h"
2 #include "semaphore.h"
3 #include <stdarg.h> /* для списков аргументов переменной длины */
4 #define MAX_TRIES 10 /* количество попыток инициализации */
5 mysem_t *
6 mysem_open(const char *pathname, int oflag, … )
7 {
8 int i, fd, semflag, semid, save_errno;
9 key_t key;
10 mode_t mode;
11 va_list ap;
12 mysem_t *sem;
13 union semun arg;
14 unsigned int value;
15 struct semid_ds seminfo;
16 struct sembuf initop;
17 /* режим доступа для sem_open() без O_CREAT не указывается; угадываем */
18 semflag = SVSEM_MODE;
19 semid = –1;
20 if (oflag & O_CREAT) {
21 va_start(ap, oflag); /* инициализируем ар последним явно указанным аргументом */
22 mode = va_arg(ap, va_mode_t);
23 value = va_arg(ap, unsigned int);
24 va_end(ap);
25 /* преобразуем в ключ, который будет идентифицировать семафор System V */
26 if ((fd = open(pathname, oflag, mode)) == –1)
27 return(SEM_FAILED);
28 close(fd);
29 if ((key = ftok(pathname, 0)) == (key_t) –1)
30 return(SEM_FAILED);
31 semflag = IPC_CREAT | (mode & 0777);
32 if (oflag & O_EXCL)
33 semflag |= IPC_EXCL;
34 /* создаем семафор System V с флагом IPC_EXCL */
35 if ((semid = semget(key, 1, semflag | IPC_EXCD) >= 0) {
36 /* OK, мы успели первыми, поэтому инициализируем нулем */
37 arg.val = 0;
38 if (semctl(semid, 0, SETVAL, arg) == –1)
39 goto err;
40 /* увеличиваем значение, чтобы sem_otime стало ненулевым */
41 if (value > SEMVMX) {
42 errno = EINVAL;
43 goto err;
44 }
45 initop.sem_num = 0;
46 initop.sem_op = value;
47 initop.sem_flg = 0;
48 if (semop(semid, &initop, 1) == –1)
49 goto err;
50 goto finish;
51 } else if (errno != EEXIST || (semflag & IPC_EXCL) != 0)
52 goto err:
53 /* иначе продолжаем выполнение */
54 }
Создание нового семафора и работа со списком аргументов переменной длины20-24 Если вызвавший процесс указывает флаг O_CREAT, мы знаем, что функции будут переданы четыре аргумента, а не два. Работа со списком аргументов переменной длины и типом данных va_mode_t обсуждалась в связи с листингом 5.17.
Создание вспомогательного файла и преобразование полного имени в ключ System V IPC25-30 Создается обычный файл с именем, указываемым при вызове функции. Это делается для того, чтобы указать его имя при вызове функции ftok для последующей идентификации семафора. Аргумент oflag, принятый от вызвавшего процесса, передается функции open для дополнительного файла, что позволяет создать его, если он еще не существует, и вернуть ошибку EEXIST, если файл существует и указан флаг O_EXCL. Дескриптор файла затем закрывается, поскольку единственная цель создания файла была в использовании его имени при вызове ftok, преобразующей полное имя в ключ System V IPC (раздел 3.2).
Создание набора семафоров System V с одним элементом32-33 Мы преобразуем константы O_CREAT и O_EXCL в соответствующие константы System V IРС_ххх и вызываем semget для создания набора семафоров System V, состоящего из одного элемента. Флаг IPC_EXCL указывается всегда, чтобы можно было определить, существовал ли семафор до вызова функции или был создан ею.
Инициализация семафора34-50 В разделе 11.2 описана фундаментальная проблема, связанная с инициализацией семафоров System V, а в разделе 11.6 приведен код, позволяющий исключить потенциальную ситуацию гонок. Здесь мы пользуемся аналогичным методом. Первый поток, который создает семафор (вспомните, что мы всегда указываем флаг IPC_EXCL), инициализирует его значением 0 с помощью команды SETVAL при вызове semctl, а затем устанавливает запрошенное вызвавшим процессом начальное значение с помощью semop. Мы можем быть уверены, что значение sem_otime семафора функцией semget устанавливается в 0 и будет изменено на ненулевое вызовом semop. Следовательно, любой поток, работающий с существующим семафором, будет знать, что он уже проинициализирован, если значение sem_otime будет отлично от 0.