Главная страница  Взаимодействие нетривиальных процессов 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 [ 87 ] 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

Листинг 10.19 (продолжение)

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

68 }

69 }

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

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

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

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

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

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

ПРИМЕЧАНИЕ

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

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

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

ПРИМЕЧАНИЕ -

Эти правила аналогичны требованиям к разделению взаимного исключения, условной переменной или блокировки чтения-записи между процессами: средство синхронизации (переменная типа pthread mutex t, pthread cond t или pthread rwlock t) должно находиться в разделяемой памяти и инициализироваться с атрибутом PTHREAD PROCESS SHARED.



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

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

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

/* родительский процесс создает именованный семафор */ mutex = Sem open(PxJpc name(NAME), 0 CREAT OJXCL. FILE MODE. 0): if ( (childpid = ForkO) == 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 требует, чтобы оно было не меньше 32 767).

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

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

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



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

pxsem/semsysconf.с

1 finclude unpipc.h

2 int

3 main(int argc. char **argv)

5 printf( SEM NSEMS MAX - %M. SEM VALUE MAX - %Шг\ .

6 Sysconf(JC SEM NSEMS MAX). Sysconf( SC SEM VALUE MAX)):

7 exit(O);

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

SEMS NSEMS MAX = 2147483647, SEM VALUE MAX = 2147483647 alpha % semsysconf

SEMS NSEMS MAX - 256, SEM VALUE MAX = 32767

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

Займемся реализацией именованных семафоров Posix с помощью каналов FIFO. Именованный семафор реализуется как канал FIFO с конкретным именем. Неотрицательное количество байтов в канале соответствует текущему значению семафора. Функция sem post помещает 1 байт в канал, а sem wait считывает его оттуда (приостанавливая выполнение процесса, если канал пуст, а именно этого мы и хотим). Функция sem open создает канал FIFO, если указан флаг 0 CREAT; открывает его дважды (один раз на запись, другой - на чтение) и при создании нового канала FIFO помещает в него некоторое количество байтов, указанное в качестве начального значения.

ПРИМЕЧАНИЕ -

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

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

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

my pxsem fi fо/semaphore.h

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

2 typedef struct {

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

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

5 } mysem t:

6 fdefine SEM MAGIC 0x89674523

7 fifdef SEMJAILED

8 fundef SEMJAILED

9 fdefine SEMJAILED ((mysemj *)(-l)) /* чтобы компилятор не выдавал

предупреждений*/

10 fendif



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 [ 87 ] 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования.