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

ЖАНРЫ

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

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

Шрифт:

50 Sem_post(&shared.nstored); /* еще один объект */

51 }

52 }

53 void *

54 consume(void *arg)

55 {

56 int i;

57 for (i = 0;;) {

58 Sem_wait(&shared.nstored); /* ожидание появления объекта для обработки */

59 Sem_wait(&shared.mutex);

60 /* критическая область */

61 Sem_post(&shared.mutex);

62 if (shared.buff[i].n == 0)

63 return(NULL);

64 Write(STDOUT_FILENO, shared.buff[i].data, shared.buff[i].n);

65 if (++i >= NBUFF)

66 i=0; /*
кольцевой буфер */

67 Sem_post(&shared.nempty); /* освободилось место для объекта */

68 }

69 }

Пустая критическая область

40-42 Критическая область, защищаемая семафором mutex, в данном примере пуста. Если бы буферы данных представляли собой связный список, здесь мы могли бы удалять буфер из списка, не конфликтуя при этом с производителем. Но в нашем примере, где мы просто переходим к следующему буферу с единственным потоком-производителем, защищать нам просто нечего. Тем не менее мы оставляем операции установки и снятия блокировки, подчеркивая, что они могут потребоваться в новых версиях кода.

Считывание данных и увеличение семафора nstored

43-49 Каждый раз, когда производитель получает пустой буфер, он вызывает функцию read. При возвращении из read увеличивается семафор nstored, уведомляя потребителя о том, что буфер готов. При возвращении функцией read значения 0 (конец файла) семафор увеличивается, а производитель завершает работу.

Поток-потребитель

57-68 Поток-потребитель записывает содержимое буферов в стандартный поток вывода. Буфер, содержащий нулевой объем данных, обозначает конец файла. Как и в потоке-производителе, критическая область, защищенная семафором mutex, пуста.

ПРИМЕЧАНИЕ

В разделе 22.3 книги [24] мы разработали пример с несколькими буферами. В этом примере производителем был обработчик сигнала SIGIO, а потребитель представлял собой основной цикл обработки (функцию dg_echo). Разделяемой переменной был счетчик nqueue. Потребитель блокировал сигнал SIGIO на время проверки или изменения счетчика.

10.12. Использование семафоров несколькими процессами

Правила совместного использования размещаемых в памяти семафоров несколькими процессами просты: сам семафор (переменная типа semt, адрес которой является первым аргументом sem_init) должен находиться в памяти, разделяемой всеми процессами, которые хотят его использовать, а второй аргумент функции sem_init должен быть равен 1.

ПРИМЕЧАНИЕ

Эти правила аналогичны требованиям к разделению взаимного исключения, условной переменной или блокировки чтения-записи между процессами: средство синхронизации (переменная типа pthread_mutex_t, pthread_cond_t или pthread_rwlock_t) должно находиться в разделяемой памяти и инициализироваться с атрибутом PTHREAD_PROCESS SHARED.

Что касается именованных семафоров, процессы всегда могут обратиться к одному и тому же семафору, указав одинаковое имя при вызове sem_open. Хотя указатели, возвращаемые sem_open отдельным процессам, могут быть различны, все функции, работающие с семафорами, будут обращаться к одному и тому же именованному семафору.

Что произойдет, если мы

вызовем функцию sem_open, возвращающую указатель на тип sem_t, а затем вызовем fork? В описании функции fork в стандарте Posix.1 говорится, что «все открытые родительским процессом семафоры будут открыты и в дочернем процессе». Это означает, что нижеследующий код верен:

sem_t *mutex; /* глобальный указатель, копируемый, при вызове fork */

/* родительский процесс создает именованный семафор */

mutex = Sem_open(Px_ipc_name(NAME), O_CREAT | O_EXCL, FILE_MODE, 0);

if ((childpid = Fork) == 0) {

 /* дочерний процесс */

 …

 Sem_wait(mutex);

 …

}

/* родительский процесс */

Sem_post(mutex);

ПРИМЕЧАНИЕ

Причина, по которой следует аккуратно относиться к передаче семафоров при порождении процессов, заключается в том, что состояние семафора может храниться в переменной типа sem_t, но для его работы может требоваться и другая информация (например, дескрипторы файлов). В следующей главе мы увидим, что семафоры System V однозначно определяются их целочисленными идентификаторами, возвращаемыми функцией semget. Любой процесс, которому известен идентификатор, может получить доступ к семафору. Вся информация о семафоре System V хранится в ядре, а целочисленный идентификатор просто указывает номер семафора ядру.

10.13. Ограничения на семафоры

Стандартом Posix определены два ограничения на семафоры:

SEM_NSEMS_MAX — максимальное количество одновременно открытых семафоров для одного процесса (Posix требует, чтобы это значение было не менее 256);

SEM_VALUE_MAX — максимальное значение семафора (Posix требует, чтобы оно было не меньше 32767).

Две эти константы обычно определены в заголовочном файле <unistd.h> и могут быть получены во время выполнения вызовом sysconf, как мы показываем ниже.

Пример: программа semsysconf

Программа в листинге 10.20 вызывает sysconf и выводит два ограничения на семафоры, зависящие от конкретной реализации. 

Листинг 10.20. Вызов sysconf для получения ограничений на семафоры

//pxsem/semsysconf.с

1 #include "unpipc.h"

2 int

3 main(int argc, char **argv)

4 {

5 printf("SEM_NSEMS_MAX = %ld, SEM_VALUE_MAX = %ld\n",

6 Sysconf(_SC_SEM_NSEMS_MAX), Sysconf(_SC_SEM_VALUE_MAX));

7 exit(0);

8 }

При запуске этой программы в наших двух тестовых системах получим следующий результат:

solaris % semsysconf

SEMS_NSEMS_MAX = 2147483647, SEM_VALUE_MAX = 2147483647

alpha % semsysconf

SEMS_NSEMS_MAX = 256, SEM_VALUE_MAX = 32767

10.14. Реализация с использованием FIFO

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