Главная страница Взаимодействие нетривиальных процессов 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, при отправке в
|
© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |