Чтение онлайн

ЖАНРЫ

UNIX: взаимодействие процессов

Стивенс Уильям Ричард

Шрифт:

Займемся реализацией именованных семафоров Posix с помощью каналов FIFO. Именованный семафор реализуется как канал FIFO с конкретным именем. Неотрицательное количество байтов в канале соответствует текущему значению семафора. Функция sem_post помещает 1 байт в канал, a sem_wait считывает его оттуда (приостанавливая выполнение процесса, если канал пуст, а именно этого мы и хотим). Функция sem_open создает канал FIFO, если указан флаг O_CREAT; открывает его дважды (один раз на запись, другой — на чтение) и при создании нового канала FIFO помещает в него некоторое количество байтов, указанное в качестве начального значения.

ПРИМЕЧАНИЕ

Этот

и последующие разделы данной главы содержат усложненный материал, который можно при первом чтении пропустить.

Приведем текст нашего заголовочного файла semaphore.h, определяющего фундаментальный тип sem_t (листинг 10.21).

Листинг 10.21. Заголовочный файл semaphore.h

//my_pxsem_fifo/semaphore.h

1 /* фундаментальный тип */

2 typedef struct {

3 int sem_fd[2]; /* два дескриптора fd: [0] для чтения, [1] для записи */

4 int sem_magic; /* магическое число */

5 } mysem_t;

6 #define SEM_MAGIC 0x89674523

7 #ifdef SEM_FAILED

8 #undef SEM_FAILED

9 #define SEM_FAILED ((mysem_t *)(-1)) /* чтобы компилятор не выдавал предупреждений*/

10 #endif

Тип данных sem_t

1-5 Новая структура данных содержит два дескриптора, один из которых предназначен для чтения из FIFO, а другой — для записи. Для единообразия мы храним оба дескриптора в массиве из двух элементов, в котором первый дескриптор всегда открыт на чтение, а второй — на запись.

Поле sem_magiс содержит значение SEM_MAGIC, если структура проинициализирована. Это значение проверяется всеми функциями, которым передается указатель на тип sem_t, чтобы гарантировать, что передан был действительно указатель на заранее инициализированную структуру, а не на произвольную область памяти. При закрытии семафора этому полю присваивается значение 0. Этот метод хотя и не совершенен, но дает возможность обнаружить некоторые ошибки при написании программ.

Функция sem_open

В листинге 10.22 приведен текст функции sem_open, которая создает новый семафор или открывает существующий.

Листинг 10.22. Функция sem_open

//my_pxsem_fifo/sem_open.с

1 #include "unpipc.h"

2 #include "semaphore.h"

3 #include <stdarg.h> /* для произвольного списка аргументов */

4 mysem_t *

5 mysem_open(const char *pathname, int oflag, …)

6 {

7 int i, flags, save_errno;

8 char c;

9 mode_t mode;

10 va_list ap;

11 mysem_t *sem;

12 unsigned int value;

13 if (oflag & O_CREAT) {

14 va_start(ap, oflag); /* ар инициализируется последним аргументом */

15 mode = va_arg(ap, va_mode_t);

16 value = va_arg(ap, unsigned int);

17 va_end(ap);

18 if (mkfifo(pathname, mode) < 0) {

19 if (errno == EEXIST && (oflag & O_EXCL) == 0)

20 oflag &= ~O_CREAT; /*
уже существует, OK */

21 else

22 return(SEM_FAILED);

23 }

24 }

25 if ((sem = malloc(sizeof(mysem_t))) == NULL)

26 return(SEM_FAILED);

27 sem->sem_fd[0] = sem->sem_fd[1] = –1;

28 if ((sem->sem_fd[0] = open(pathname, O_RDONLY | O_NONBLOCK)) < 0)

29 goto error;

30 if ((sem->sem_fd[1] = open(pathname, O_WRONLY | O_NONBLOCK)) < 0)

31 goto error;

32 /* отключение неблокируемого режима для sem_fd[0] */

33 if ((flags = fcntl(sem->sem_fd[0], F_GETFL, 0)) < 0)

34 goto error;

35 flags &= ~O_NONBLOCK;

36 if (fcntl(sem->sem_fd[0], F_SETFL, flags) < 0)

37 goto error;

38 if (oflag & O_CREAT) { /* инициализация семафора */

39 for (i = 0; i < value; i++)

40 if (write(sem->sem_fd[1], &c, 1) != 1)

41 goto error;

42 }

43 sem->sem_magic = SEM_MAGIC;

44 return(sem);

45 error:

46 save_errno = errno;

47 if (oflag & O_CREAT)

48 unlink(pathname); /* если мы создали FIFO */

49 close(sem->sem_fd[0]); /* игнорируем ошибку */

50 close(sem->sem_fd[1]); /* игнорируем ошибку */

51 free(sem);

52 errno = save_errno;

53 return(SEM_FAILED);

54 }

Создание нового sсемафора

13-17 Если при вызове указан флаг O_CREAT, должно быть указано четыре аргумента, а не два. Мы вызываем va_start, после чего переменная ар указывает на последний явно указанный аргумент (oflag). Затем мы используем ар и функцию va_arg для получения значений третьего и четвертого аргументов. Работу со списком аргументов переменной длины и использование нашего типа va_mode_t мы обсуждали в связи с листингом 5.17.

Создание нового канала FIFO

18-23 Создается новый канал FIFO, имя которого было указано при вызове функции. Как мы отмечали в разделе 4.6, эта функция возвращает ошибку EEXIST, если канал уже существует. Если при вызове sem_open флаг O_EXCL не был указан, мы пропускаем эту ошибку; но нам не нужно будет инициализировать этот канал, так что мы при этом сбрасываем флаг O_CREAT.

Поделиться с друзьями: