Уильям Стивенс - UNIX: взаимодействие процессов
38-42 Если мы создали семафор, его нужно проинициализировать, записав в канал FIFO value байтов. Если указанное при вызове значение value превышает определенное реализацией ограничение PIPE_BUF, вызов write после переполнения FIFO вернет ошибку с кодом EAGAIN.
Функция sem_close
Текст функции sem_close приведен в листинге 10.23.
11-15 Мы закрываем оба дескриптора и освобождаем память, выделенную под тип sem_t.
Листинг 10.23. Функция sem_close//my_pxsem_fifo/sem_close.с
1 #include "unpipc.h"
2 #include "semaphore.h"
3 int
4 mysem_close(mysem_t *sem)
5 {
6 if (sem->sem_magic != SEM_MAGIC) {
7 errno = EINVAL;
8 return(-1);
9 }
10 sem->sem_magic = 0; /* чтобы семафор нельзя было больше использовать */
11 if (close(sem->sem_fd[0]) == –1 || close(sem->sem_fd[1]) == –1) {
12 free(sem);
13 return(-1);
14 }
15 free(sem);
16 return(0);
17 }
Функция sem_unlink
Функция sem_unlink, текст которой приведен в листинге 10.24, удаляет из файловой системы наш семафор. Она просто вызывает unlink.
Листинг 10.24. Функция sem_unlink//my_pxsem_fifo/sem_unlink. с
1 #include "unpipc.h"
2 #include "semaphore.h"
3 int
4 mysem_unlink(const char *pathname)
5 {
6 return(unlink(pathname));
7 }
Функция sem_post
В листинге 10.25 приведен текст функции sem_post, которая увеличивает значение семафора.
11-12 Мы записываем один байт в FIFO. Если канал был пуст, это приведет к возобновлению выполнения всех процессов, заблокированных в вызове read для этого канала.
Листинг 10.25. Функция sem_post//my_pxsem_fifo/sem_post.с
1 #include "unpipc.h"
2 #include "semaphore.h"
3 int
4 mysem_post(mysem_t *sem)
5 {
6 char c;
7 if (sem->sem_magic != SEM_MAGIC) {
8 errno = EINVAL;
9 return(-1);
10 }
11 if (write(sem->sem_fd[1], &c, 1) == 1)
12 return(0);
13 return(-1);
14 }
Функция sem_wait
Последняя функция для работы с именованными семафорами Posix — sem_wait. Ее текст приведен в листинге 10.26.
Листинг 10.26. Функция sem_wait//my_pxsem_fifo/sem_wait.с
1 #include "unpipc.h"
2 #include "semaphore.h"
3 int
4 mysem_wait(mysem_t *sem)
5 {
6 char c;
7 if (sem->sem_magic != SEM_MAGIC) {
8 errno = EINVAL;
9 return(-1);
10 }
11 if (read(sem->sem_fd[0], &c, 1) == 1)
12 return(0);
13 return(-1);
14 }
11-12 Мы считываем 1 байт из канала FIFO, причем работа приостанавливается, если канал пуст.
Мы еще не реализовали функцию sem_trywait, но это можно сделать, установив флаг отключения блокировки для канала и используя обычный вызов read. Мы также не реализовали функцию sem_getvalue. В некоторых реализациях при вызове функции stat или fstat возвращается количество байтов в именованном или неименованном канале, причем оно помещается в поле st_size структуры stat. Однако это не гарантируется стандартом Posix и, следовательно, не обязательно будет работать в других системах. Пример реализации этих двух функций для работы с семафорами Posix приведен в следующем разделе.
10.15. Реализация с помощью отображения в память
Теперь займемся реализацией именованных семафоров Posix с помощью отображаемых в память файлов вместе со взаимными исключениями и условными переменными Posix. Реализация, аналогичная данной, приведена в разделе В.11.3 Обоснования стандарта IEEE 1996 [8].
ПРИМЕЧАНИЕ
Отображаемые в память файлы описаны в главах 12 и 13. Данный раздел можно отложить, с тем чтобы вернуться к нему после прочтения этих глав.
Прежде всего приведем текст нашего заголовочного файла semaphore.h (листинг 10.27), в котором определяется фундаментальный тип sem_t.
Тип sem_t1-7 Структура данных семафора содержит взаимное исключение, условную переменную и беззнаковое целое, в котором хранится текущее значение семафора. Как уже говорилось в связи с листингом 10.21, поле sem_magiс получает значение SEM_MAGIC при инициализации структуры.
Листинг 10.27. Заголовочный файл semaphore.h//my_pxsem_mmap/semaphore.h
1 /* фундаментальный тип */
2 typedef struct {
3 pthread_mutex_t sem_mutex; /* блокируется при проверке и изменении значения семафора */
4 pthread_cond_t sem_cond; /* при изменении нулевого значения */
5 unsigned int sem_count; /* значение семафора */
6 int sem_magic; /* магическое значение, если семафор открыт */
7 } mysem_t;
8 #define SEM_MAGIC 0x67458923
9 #ifdef SEM_FAILED
10 #undef SEM_FAILED
11 #define SEM_FAILED ((mysem_t *)(-1)) /* чтобы избежать предупреждений компилятора */
12 #endif
Функция sem_open
В листинге 10.28 приведен текст первой части функции sem_open, которая может использоваться для создания нового семафора или открытия существующего.
Листинг 10.28. Функция sem_open: первая половина//my_pxsem_mmap/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 fd, i, created, save_errno;
9 mode_t mode;
10 va_list ap;
11 mysem_t *sem, seminit;
12 struct stat statbuff;
13 unsigned int value;
14 pthread_mutexattr_t mattr;
15 pthread_condattr_t cattr;
16 created = 0;
17 sem = MAP_FAILED; /* [sic] */
18 again:
19 if (oflag & O_CREAT) {
20 va_start(ap, oflag); /* ар инициализируется последним явно указанным аргументом */
21 mode = va_arg(ap, va_mode_t) & ~S_IXUSR;
22 value = va_arg(ap, unsigned int);
23 va_end(ap);
24 /* открываем с указанием флага O_EXCL и установкой бита user-execute */
25 fd = open(pathname, oflag | O_EXCL | O_RDWR, mode | S_IXUSR);
26 if (fd < 0) {
27 if (errno == EEXIST && (oflag & O_EXCL) == 0)
28 goto exists; /* уже существует. OK */
29 else
30 return(SEM_FAILED);
31 }
32 created = 1;
33 /* кто создает файл, тот его и инициализирует */
34 /* установка размера файла */
35 bzero(&seminit, sizeof(seminit));
36 if (write(fd, &seminit, sizeof(seminit)) != sizeof(seminit))
37 goto err;
38 /* отображение файла в память */
39 sem = mmap(NULL, sizeof(mysem_t), PROT_READ | PROT_WRITE,
40 MAP_SHARED, fd, 0);
41 if (sem == MAP_FAILED)
42 goto err;
43 /* инициализация взаимного исключения, условной переменной, значения семафора */
44 if ((i = pthread_mutexattr_init(&mattr)) != 0)
45 goto pthreaderr;
46 pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
47 i = pthread_mutex_init(&sem->sem_mutex, &mattr);
48 pthread_mutexattr_destroy(&mattr); /* не забыть удалить */
49 if (i != 0)
50 goto pthreaderr;
51 if ((i = pthread_condattr_init(&cattr)) != 0)
52 goto pthreaderr;
53 pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
54 i = pthread_cond_init(&sem->sem_cond, &cattr);
55 pthread_condattr_destroy(&cattr); /* не забыть удалить */
56 if (i != 0)
57 goto pthreaderr;
58 if ((sem->sem_count = value) > sysconf(_SC_SEM_VALUE_MAX)) {
59 errno = EINVAL;
60 goto err;
61 }
62 /* инициализация завершена, снимаем бит user-execute */
63 if (fchmod(fd, mode) == –1)
64 goto err;
65 close(fd);
66 sem->sem_magic = SEM_MAGIC;
67 return(sem);
68 }
Работа со списком аргументов переменной длины19-23 Если при вызове функции указан флаг O_CREAT, мы должны принять четыре аргумента, а не два. Работа со списком аргументов переменной длины с помощью типа va_mode_t уже обсуждалась в связи с листингом 5.17, где мы использовали метод, аналогичный примененному здесь. Мы сбрасываем бит user-execute переменной mode (S_IXUSR) по причинам, которые вскоре будут раскрыты. Создается файл с указанным именем, и для него устанавливается бит user-execute.
Создание нового семафора и обработка потенциальной ситуации гонок24-32 Если бы при указании флага O_CREAT мы просто открывали файл, отображали в память его содержимое и инициализировали поля структуры sem_t, у нас возникла бы ситуация гонок. Эта ситуация также уже обсуждалась в связи с листингом 5.17, и там мы воспользовались тем же методом, что и сейчас. Такая же ситуация гонок встретится нам, когда мы будем разбираться с листингом 10.37.