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

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

23 while ( (n = Mesg recv(readid. &niesg)) > 0)

24 WriteCSTDOUTJILENO. mesg.mesgjata, n):

25 }

В листинге6.17 приведен текст функции client. Эта функция практически идентична функции из листинга 6.15, но вместо передачи идентификатора процесса клиента на сервер направляется идентификатор очереди клиента. Тип сообщения в структуре mesg остается равным 1, поскольку это значение устанавливается для сообщений, передаваемых в обе стороны.

В листинге 6.19 приведена функция server. Главное отличие от листинга 6.13 в том, что эта функция представляет собой бесконечный цикл, в котором для каждого нового клиента вызывается fork.

Установка обработчика сигнала для SIGCHLD

10 Поскольку для каждого клиента порождается отдельный процесс, нужно позаботиться о процессах-зомби. В разделах 5.9 и 5.10 [24] об этом говорится подробно. Здесь мы просто установим обработчик для сигнала SIGCHLD, и наша функция sig chld (листинг 6.18) будет вызываться при завершении работы дочернего процесса.

12-18 Породивший процесс сервера блокируется в вызове mesg recv, ожидая появления сообщения от очередного клиента.

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

Функция-обработчик для SIGCHLD приведена в листинге 6.18. Она скопирована с листинга 5.11 [24].

Листинге.18. Обработчик сигнала SIGCHLD, вызывающий waitpid

svmsgmpxnq/sigchldwaitpid.c

1 #include unpipc.h

2 void

3 sig chld(int signo)

5 pid t pid:

6 int stat:

7 while ( (pid = waitpid(-l. &stat. WNOHANG)) > 0) :

8 return;

Каждый раз при вызове обработчика происходит циклический вызов waitpid для получения статуса завершения работы всех дочерних процессов, которые могли завершить работу. Затем происходит возврат из обработчика сигнала. При этом может возникнуть проблема, поскольку родительский процесс проводит большую часть времени в заблокированном состоянии (при вызове mesg recv, листинг 6.9). При возвращении из обработчика этот вызов msgrcv прерывается. Функция возвращает ошибку с кодом EINTR, как рассказывается в разделе 5.9 [24].



Нам нужно обработать такой возврат из вызванной функции, поэтому мы пишем новую функцию-обертку Mesg recv, приведенную в листинге 6.20. Эта программа допускает возвращение ошибки с кодом EINTR функцией mesg recv (которая просто вызывает msgrcv), и, если это происходит, мы просто еще раз вызываем mesg recv.

Листинг 6.19. Функция server

Svmsgmpxnq/server.с

1 #inclucle mesg.h

2 void

3 serverCint readid, int writeid)

5 FILE *fp:

6 char *ptr:

7 ssize t П;

8 struct mymesg mesg;

9 void sig chldCint);

10 SignalCSIGCHLD, sig chld):

11 for С ; : ) {

12 /* считывание имени файла из очереди */

13 mesg.mesg type = 1:

14 if С Cn = Mesg recvCreadid, &mesg)) == 0) {

15 err msgC pathname missing );

16 continue:

17 }

18 mesg.mesg data[n] = \0; /* имя файла */

19 if С Cptr = strchrCmesg.mesg data. )) == NULL) {

20 err msgC bogus request: s , mesg.mesg data):

21 continue;

22 }

23 *ptr++ =0; /* ptr = имя файла */

24 writeid = atoiCmesg.mesg data);

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

26 if С Cfp = fopenCptr. r )) == NULL) {

27 /* ошибка: нужно сообщить клиенту */

28 snprintfCmesg.mesg data + п. sizeofCmesg.mesg data) - n,

29 : cant open, s\n , strerrorCerrno));

30 mesg.mesgjen = strlenCptr);

31 memmoveCmesg.mesg data. ptr. mesg. mesg Jen);

32 Mesg sendCwriteid. &mesg);

33 } else {

34 /* файл открыт, копируем клиенту */

35 while CFgetsCmesg.mesg data. MAXMESGDATA. fp) != NULL) {

36 mesg.mesgjen = strlenCmesg.mesg data):

37 Mesg sendСwriteid. &mesg):

38 }

39 FcloseCfp):

40 }

41 /* отправка сообщения нулевой длины, указывающего конец файла */



42 mesg.mesgjen = 0:

43 Mesg sencl(writeid. &mesg):

44 exitCO): /* завершение дочернего процесса */

45 }

46 /* родительский процесс просто зациклен */

47 }

48 }

Листинг 6.20. Функция-обертка Mesg recv, обрабатывающая прерванный системный вызов

svmsgmpxnq/mesg recv.c

10 ssizej

И Mesg recvCint id. struct mymesg *mptr)

12 {

13 ssizej n:

14 do {

15 n = mesg recv(id. mptr):

16 } while (n -= -1 && errno == EINTR);

17 if (n == -1)

18 err sys( mesg recv error );

19 return(n);

20 }

6.9. Использование select и poll с очередями сообщений

Одним из недостатков очередей сообщений System V является то, что они идентифицируются не дескрипторами, а идентификаторами. Поэтому с ними нельзя использовать функции select и pol 1 (глава 6 [24]).

ПРИМЕЧАНИЕ -

На самом деле одна из версий Unix, а именно AIX (созданная IBM), позволяет использовать select с очередями сообщений System V, а не только с дескрипторами. Но эта возможность имеется только в AIX.

Этот недостаток часто всплывает, когда возникает необходимость написать сервер, работающий одновременно с сетевыми соединениями и с IPC. Сетевые соединения с использованием интерфейса сокетов или XTI ([24]) используют дескрипторы, что позволяет вызывать select или poll. Программные каналы и FIFO также идентифицируются дескрипторами, поэтому для них тоже допустимо использование этих функций.

Одним из решений этой проблемы является следующее: сервер должен создать канал и породить процесс, который будет заблокирован при вызове msgrcv. При получении сообщения произойдет возврат из msgrcv, дочерний процесс получит это сообщение из очереди и запишет его в канал. Затем родительский процесс может использовать функцию select для канала совместно с сетевыми соединениями. Недостаток этого подхода в том, что сообщения обрабатываются трижды: при считывании дочерним процессом с помощью msgrcv, при отправке в



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.
Копирование материалов разрешено исключительно при условии цититирования.