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

ЖАНРЫ

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

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

Шрифт:

10 initflag = 1;

11 }

12 Sem_wait(locksem);

13 }

14 void

15 my_unlock(int fd)

16 {

17 Sem_post(locksem);

18 }

Один из семафоров используется для рекомендательной блокировки доступа к файлу и инициализируется единицей при первом вызове функции. Для получения блокировки мы вызываем sem_wait, а для ее снятия — sem_post.

10.8. Функции sem_init и sem_destroy

До сих пор мы имели дело только с именованными

семафорами Posix. Как мы уже говорили, они идентифицируются аргументом пате, обычно представляющим собой имя файла в файловой системе. Стандарт Posix описывает также семафоры, размещаемые в памяти, память под которые выделяет приложение (тип sem_t), а инициализируются они системой:

#include <semaphore.h>

int sem_init(sem_t *sem, int shared, unsigned int value);

/* Возвращает –1 в случае ошибки */

int sem_destroy(sem_t *sem);

/* Возвращает 0 в случае успешного завершения, –1 – в случае ошибки */

Размещаемый в памяти семафор инициализируется вызовом sem_init. Аргумент sem указывает на переменную типа sem_t, место под которую должно быть выделено приложением. Если аргумент shared равен 0, семафор используется потоками одного процесса, в противном случае доступ к нему могут иметь несколько процессов. Если аргумент shared ненулевой, семафор должен быть размещен в одном из видов разделяемой памяти и должен быть доступен всем процессам, использующим его. Как и в вызове sem_open, аргумент value задает начальное значение семафора.

После завершения работы с размещаемым в памяти семафором его можно уничтожить, вызвав sem_destroy.

ПРИМЕЧАНИЕ 1

Функции sem_open не требуется параметр, аналогичный shared; не требуется ей и атрибут, аналогичный PTHREAD_PROCESS_SHARED (упоминавшийся в связи с взаимными исключениями и условными переменными в главе 7), поскольку именованный семафор всегда используется совместно несколькими процессами.

ПРИМЕЧАНИЕ 2

Обратите внимание, что для размещаемого в памяти семафора нет ничего аналогичного флагу O_CREAT: функция sem_init всегда инициализирует значение семафора. Следовательно, нужно быть внимательным, чтобы вызывать sem_init только один раз для каждого семафора. (Упражнение 10.2 иллюстрирует разницу в этом смысле между именованным и размещаемым в памяти семафорами.) При вызове sem_init для уже инициализированного семафора результат непредсказуем.

ПРИМЕЧАНИЕ 3

Удостоверьтесь, что вы понимаете фундаментальную разницу между sem_open и sem_init. Первая возвращает указатель на переменную типа sem_t, причем выделение места под переменную и ее инициализация выполняются этой же функцией. Напротив, первый аргумент sem_init представляет собой указатель на переменную типа sem_t, место под которую должен был заранее выделить вызывающий. Функция sem_init только инициализирует эту переменную.

ПРИМЕЧАНИЕ 4

Стандарт Posix.1 предупреждает, что для обращения к размещаемым в памяти семафорам можно использовать только указатель, являющийся аргументом при вызове sem_init. Использование копий этого указателя может привести к неопределенным результатам.

Функция sem_init возвращает –1 в случае ошибки, но она не возвращает 0 в случае успешного завершения. Это действительно странно, и примечание в Обосновании Posix. 1 говорит, что в будущих версиях функция, возможно, начнет возвращать 0 в случае успешного завершения. 

Размещаемый в памяти семафор может быть использован в тех случаях, когда нет необходимости использовать имя, связываемое с именованным семафором. Именованные семафоры

обычно используются для синхронизации работы неродственных процессов. Имя в этом случае используется для идентификации семафора.

В связи с табл. 1.1 мы говорили о том, что семафоры, размещаемые в памяти, обладают живучестью процесса, но на самом деле их живучесть зависит от типа используемой разделяемой памяти. Размещаемый в памяти семафор не утрачивает функциональности до тех пор, пока память, в которой он размещен, еще доступна какому-либо процессу.

■ Если размещаемый в памяти семафор совместно используется потоками одного процесса (аргумент shared при вызове sem_init равен 0), семафор обладает живучестью процесса и удаляется при завершении последнего.

■ Если размещаемый в памяти семафор совместно используется несколькими процессами (аргумент shared при вызове seminit равен 1), он должен располагаться в разделяемой памяти, и в этом случае семафор существует столько, сколько существует эта область памяти. Вспомните, что и разделяемая память Posix, и разделяемая память System V обладают живучестью ядра (табл. 1.1). Это значит, что сервер может создать область разделяемой памяти, инициализировать в ней размещаемый в памяти семафор Posix, а затем завершить работу. Некоторое время спустя один или несколько клиентов могут присоединить эту область к своему адресному пространству и получить доступ к хранящемуся в ней семафору.

Предупреждаем, что нижеследующий код не работает так, как ожидается:

sem_t mysem;

Sem_init(&mysem, 1.0); /* 2-й аргумент 1 –> используется процессами */

if (Fork == 0) { /* дочерний процесс */

 …

 Sem_post(&mysem);

}

Sem_wait(&mysem); /* родительский процесс: ожидание дочернего */

Проблема тут в том, что семафор не располагается в разделяемой памяти (см. раздел 10.12). Память, как правило, не делится между дочерним и родительским процессами при вызове fork. Дочерний процесс запускается с копией памяти родителя, но это не то же самое, что разделяемая память.

Пример

В качестве иллюстрации перепишем наш пример решения задачи производителей и потребителей из листингов 10.8 и 10.9 для использования размещаемых в памяти семафоров Posix. В листинге 10.11 приведен текст новой программы.

Листинг 10.11. Задача производителей и потребителей с использованием размещаемых в памяти семафоров

//pxsem/prodcons2.c

1 #include "unpipc.h"

2 #define NBUFF 10

3 int nitems; /* только для чтения производителем и потребителем */

4 struct { /* общие данные производителя и потребителя */

5 int buff[NBUFF];

6 sem_t mutex, nempty, nstored; /* семафоры, а не указатели */

7 } shared;

8 void *produce(void *), *consume(void *);

9 int

10 main(int argc, char **argv)

11 {

12 pthread_t tid_produce, tid_consume;

13 if (argc != 2)

14 err_quit("usage: prodcons2 <#items>");

15 nitems = atoi(argv[1]);

16 /* инициализация трех семафоров */

17 Sem_init(&shared.mutex, 0, 1);

18 Sem_init(&shared.nempty, 0, NBUFF);

19 Sem_init(&shared.nstored, 0, 0);

20 Set_concurrency(2);

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