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

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

ции с возвратом ошибки EAGAIN. Поскольку установлен флаг 0 NONBLOCK, процесс не может быть заблокирован, но в то же время ядро не может принять лишь часть данных, так как при этом невозможно гарантировать атомарность операции записи. Поэтому ядро возвращает ошибку, сообщающую процессу о необходимости попытаться произвести запись еще раз. ш Если количество байтов превышает значение PIPEBUF, то:

□ Если в программном канале или FIEO есть место хотя бы для одного байта, ядро передает в буфер ровно столько данных, сколько туда может поместиться, и это переданное количество возвращается функцией wri te.

□ Если в программном канале или FIFO свободное место отсутствует, происходит немедленное завершение работы с возвратом ошибки EAGAIN.

* При записи в программный канал или FIFO, не открытый для чтения, ядро посылает сигнал SIGPIPE:

□ Если процесс не принимает (catch) и не игнорирует SIGPIPE, выполняется действие по умолчанию - завершение работы процесса.

□ Если процесс игнорирует сигнал SIGPIPE или перехватывает его и возвращается из подпрограммы его обработки, write возвращает ошибку с кодом EPIPE.

ПРИМЕЧАНИЕ-

SIGPIPE считается синхронным сигналом, что означает, что он привязан к конкретному программному потоку, а именно тому, который вызвал функцию write. Простейшим способом обработки сигнала является его игнорирование (установка SIG IGN) и предоставление функции write возможности вернуть ошибку с кодом EPIPE. В приложении всегда должна быть предусмотрена обработка ошибок, возвращаемых функцией write, а вот определить, что процесс был завершен сигналом SIGPIPE, сложнее. Если сигнал не перехватывается, придется посмотреть на статус завершения работы процесса (termination status) из интерпретатора команд, чтобы узнать, что процесс был принудительно завершен сигналом и каким именно сигналом. В разделе 5.13 [24] о сигнале SIGPIPE рассказывается более подробно.

4.8. Один сервер, несколько клиентов

Преимущества канала FIFO проявляются более явно в том случае, когда сервер представляет собой некоторый длительно функционирующий процесс (например, демон, наподобие описанного в главе 12 [24]), не являющийся родственным клиенту. Демон создает именованный канал с вполне определенным известным именем, открывает его на чтение, а запускаемые впоследствии клиенты открывают его на запись и отправляют демону команды и необходимые данные. Односторонняя связь в этом направлении (от клиента к серверу) легко реализуется с помощью FIFO, однако необходимость отправки данных в обратную сторону (от сервера к клиенту) усложняет задачу. Рисунок 4.12 иллюстрирует прием, применяемый в этом случае.

Сервер создает канал с известным полным именем, в данном случае /tmp/ f i f о. serv. Из этого канала он считывает запросы клиентов. Каждый клиент при



/tmp/fifb.1234

read-only

клиент 1


Лтр/ fifb.9876

FIFO

FIFO

read-only

клиент 2

PID1234

Рис. 4.12. Один сервер, несколько клиентов

PID 9876

запуске создает свой собственный канал, полное имя которого определяется его идентификатором процесса. Клиент отправляет свой запрос в канал сервера с известным именем, причем запрос этот содержит идентификатор процесса клиента и имя файла, отправку которого клиент запрашивает у сервера. В листинге 4.10 приведен текст программы сервера.

Листинг 4.10. Сервер, обслуживающий несколько клиентов с помощью канала FIFO

fi foci i serv/mai nserver.с 1 #include fifo.h

10 11 12

13 14

16 17 18 19

void serverCint. int);

rainCint argc. char **argv) {

int readflfo. writefifo. dumnyfd. fd; char *ptr. buff [MAXLINE], fifonanie[MAXLINE]; pid t pid: ssize t n;

/* создание FIFO сервера с известным именем, OK, если уже существует */ if ((mkfifoCSERVJIFO. FILE MOOE) < 0) && (еггпо !- EEXIST)) err sys( cant create %s\ SERVJIFO);

/* открытие FIFOcepBepa на чтение */ readflfo - Open (SERVJIFO. 0 RroNLY. 0): dumrnyfd - Open(S£RV FIFO. 0 WRONLY. 0);

/* не используется */

while ( (n - Readline(readfifo. buff. MAXLINE)) > if (buff[n-l] - Лп-)

n--; /* delete newline from readlineO */

buff[n] = NO: /* полное имя, завершаемое О */



20 if ( (ptr - strchrCbuff. )) NULL) {

21 err msg( bogus request: *s . buff):

22 continue:

23 }

24 *ptr++ - 0: /* идентификатор процесса, указатель на имя файла */

25 pid - atol(buff):

26 snprintfCfifoname. sizeof(fifoname). /tmp/fifo.Xld . (long) pid):

27 if ( (writefifo - openCfifoname. 0 WRONLY. 0)) < 0) {

28 err msg( cannot open: %s°. fifoname):

29 continue:

30 }

31 if ( (fd - openCptr. 0 RDONLY)) < 0) {

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

33 snprintf(buff + п. sizeof(buff) - n. : cant open. *s\n .

34 strerror(errno)):

35 n - strlen(ptr):

36 Write(writefifo. ptr. n):

37 Close(writefifo): 38

39 } else {

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

41 while ( (n - Read(fd. buff. MAXLINE)) > 0)

42 Write(writefifo. buff, n):

43 Close(fd):

44 Close(writefifo):

45 }

46 }

47 }

Создание канала и открытие его только для записи и только для чтения

10-15 Сервер создает канал FIFO с известным именем, обрабатывая ситуацию, когда такой канал уже существует. Затем этот канал открывается дважды: один раз только для чтения, а второй - только для записи. Дескриптор readf i fo используется для приема запросов от клиентов, а дескриптор dumiiyfd не используется вовсе. Причина, по которой нужно открыть канал для записи, видна из табл. 4.1. Если канал не открыть на запись, то при завершении работы очередного клиента этот канал будет опустошаться и сервер будет считывать О, означающий конец файла Пришлось бы каждый раз закрывать канал вызовом close, а затем заново открывать его с флагом 0 RDONL Y, что приводило бы к блокированию демона до подключения следующего клиента. Мы же всегда будем иметь дескриптор, открытый на запись, поэтому функция read не будет возвращать О, означающий конец файла, при отсутствии клиентов. Вместо этого сервер просто будет блокироваться при вызове read, ожидая подключения следующего клиента. Этот трюк упрощает код программы-сервера и уменьшает количество вызовов open для канала сервера.

При запуске сервера первый вызов open (с флагом 0 RDONLY) приводит к блокированию процесса до появления первого клиента, открывающего канал сервера на запись (см. табл. 4.1). Второй вызов open (с флагом OWRONLY) не приводит к блокированию, поскольку канал уже открыт на запись.



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