Уильям Стивенс - UNIX: взаимодействие процессов
- Название:UNIX: взаимодействие процессов
- Автор:
- Жанр:
- Издательство:Питер
- Год:2003
- Город:Санкт-Петербург
- ISBN:5-318-00534-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: взаимодействие процессов краткое содержание
Книга написана известным экспертом по операционной системе UNIX и посвящена описанию одной из форм межпроцессного взаимодействия, IPC, с использованием которой создается большинство сложных программ. В ней описываются четыре возможности разделения решаемых задач между несколькими процессами или потоками одного процесса: передача сообщений, синхронизация, разделяемая память, удаленный вызов процедур.
Книга содержит большое количество иллюстрирующих примеров и может использоваться как учебник по IPC, и как справочник для опытных программистов.
UNIX: взаимодействие процессов - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Открытие файла и отправка его в FIFO клиента
27-44 Оставшаяся часть кода пpoгрaммы-cepвepa аналогична функции server из листинга 4.3. Программа открывает файл; если при этом возникает ошибка — клиенту отсылается сообщение о ней. Если открытие файла завершается успешно, его содержимое копируется в канал клиента. После завершения копирования открытый сервером «конец» (дескриптор) канала клиента должен быть закрыт с помощью функции close, чтобы функция read вернула пpoгрaммe-клиeнтy значение 0 (конец файла). Сервер не удаляет канал клиента; клиент должен самостоятельно позаботиться об этом после приема от сервера символа конца файла. Текст пpoгрaммы-клиeнтa приведен в листинге 4.11.
//fifocliserv/mainclient.с
1 #include "fifo.h"
2 int
3 main(int argc, char **argv)
4 {
5 int readfifo, writefifo;
6 size_t len;
7 ssize_t n;
8 char *ptr, fifoname[MAXLINE], buff[MAXLINE];
9 pid_t pid;
10 /* создание FIFO с включением в его имя PID */
11 pid = getpid();
12 snprintf(fifoname, sizeof(fifoname), "/tmp/fifo,%ld", (long) pid):
13 if ((mkfifo(fifoname, FILE_MODE) < 0) && (errno != EEXIST))
14 err_sys("can't create %s", fifoname);
15 /* инициализация буфера PID и пробелом */
16 snprintf(buff, sizeof(buff), "%ld ", (long) pid);
17 len = strlen(buff);
18 ptr = buff + len;
19 /* считывание полного имени */
20 Fgets(ptr, MAXLINE – len, stdin);
21 len = strlen(buff); /* fgets() гарантирует завершающий 0 */
22 /* открытие FIFO сервера и запись в него полного имени и PID */
23 writefifo = Open(SERV_FIFO, O_WRONLY, 0);
24 Write(writefifo, buff, len);
25 /* открытие созданного FIFO; блокирование до открытия его сервером */
26 readfifo = Open(fifoname, O_RDONLY; 0);
27 /* считывание из канала IPC, запись в stdout */
28 while ((n = Read(readfifo, buff, MAXLINE)) > 0)
29 Write(STDOUT_FILENO, buff, n);
30 Close(readfifo);
31 Unlink(fifoname);
32 exit(0);
33 }
10-14 Идентификатор процесса клиента содержится в имени создаваемого им канала.
15-21 Запрос клиента состоит из его идентификатора процесса, одного пробела, полного имени запрашиваемого им файла и символа перевода строки. Строка запроса формируется в массиве buff, причем имя файла считывается из стандартного потока ввода.
22-24 Клиент открывает канал сервера и записывает в него строку запроса. Если клиент окажется первым с момента запуска сервера, вызов open разблокирует сервер, заблокированный после сделанного им вызова open (с флагом O_RDONLY).
25-31 Ответ сервера считывается из канала и записывается в стандартный поток вывода, после чего канал клиента закрывается и* удаляется.
Сервер может быть запущен в одном из окон, а клиент — в другом, и программа будет работать так, как мы и рассчитывали. Ниже мы приводим только текст, выводимый клиентом:
solaris % mainclient /etc/shadow файл, который нам нельзя читать
/etc/shadow: can't open. Permission denied
solaris % mainclient /etc/inet/ntp.conf файл из двух строк
multicastclient 224.0.1.1
driftfile /etc/inet/ntp.drift
Мы можем также связаться с сервером из интерпретатора команд, поскольку каналы FIFO обладают именами в файловой системе.
solaris % Pid=$$
solaris % mkfifo /tmp/fifo.$Pid
solaris % echo "$Pid /etc/inet/ntp.conf" > /tmp/fifo.serv
solaris % cat < /tmp/fifo.$Pid
multicastclient 224.0.1.1
driftfile /etc/inet/ntp.drift
solaris % rm /tmp/fifo.$Pid
Мы отсылаем серверу идентификатор процесса текущей копии интерпретатора и полное имя файла одной командой интерпретатора (echo) и считываем из канала сервера результат с помощью другой команды (cat). Между выполнением этих двух команд может пройти произвольный промежуток времени. Таким образом, сервер помещает содержимое файла в канал, а клиент затем запускает команду cat, чтобы считать оттуда данные. Может показаться, что данные каким-то образом хранятся в канале, хотя он не открыт ни одним процессом. На самом деле все не так. После закрытия пpoгрaммнoгo канала или FIFO последним процессом с помощью команды close все данные, в нем находящиеся, теряются. В нашем примере сервер, считав строку запроса от клиента, блокируется при попытке открыть канал клиента, потому что клиент (наша копия интерпретатора) еще не открыл его на чтение (вспомним табл. 4.1). Только после вызова cat некоторое время спустя канал будет открыт на чтение, и тогда сервер разблокируется. Кстати, таким образом осуществляется атака типа «отказ в обслуживании» (denial-of-service attack), которую мы обсудим в следующем разделе.
Использование интерпретатора позволяет провести простейшую проверку способности сервера обрабатывать ошибки. Мы можем отправить серверу строку без идeнтификaтopa процесса или отослать ему такой идентификатор, которому не соответствует никакой канал FIFO в каталоге /tmp. Например, если мы запустим сервер и введем нижеследующие строки:
solaris % cat > /tmp/fifo.serv /no/process/id
999999 /invalid/process/id
то сервер выдаст текст:
solaris % server
bogus request: /no/process/id
cannot open: /tmp/fifo.999999
Атомарность записи в FIFO
Наша простейшая пара клиент-сервер позволяет наглядно показать важность наличия свойства атомарности записи в пpoгрaммныe каналы и FIFO. Предположим, что два клиента посылают серверу запрос приблизительно в один и тот же момент. Первый клиент отправляет следующую строку:
1234 /etc/inet/ntp.conf
второй:
9876 /etc/passwd
Предполагая, что каждый клиент помещает данные в FIFO за один вызов write и кaждая строка имеет размер, не превышающий величины PIPE_BUF (что чаще всего заведомо выполняется, поскольку PIPE_BUF обычно лежит в диапазоне 1024-5120, а длина полного имени обычно oгрaничeнa 1024 байт), мы можем гарантировать, что в FIFO данные будут иметь следующий вид:
1234 /etc/inet/ntp.conf
9876 /etc/passwd
либо
9876 /etc/passwd
1234 /etc/inet/ntp.conf
Данные в канале не могут смешаться в «кашу», наподобие:
1234 /etc/inet9876 /etc/passwd
/ntp.conf
FIFO и NFS
Каналы FIFO представляют собой вид IPC, который может использоваться только в пределах одного узла. Хотя FIFO и обладают именами в файловой системе, они могут применяться только в локальных файловых системах, но не в присоединенных сетевых (NFS).
solaris % mkfifo /nsf/bsdi/usr/rstevens/fifo.temp
mkfifo: I/O error
В этом примере файловая система /nfs/bsdi/usr — это файловая система /usr нa yзлe bsdi.
Некоторые системы (например, BSD/OS) позволяют создавать FIFO в присоединенных файловых системах, но по ним нельзя передавать данные между узлами. В этом случае такой канал может использоваться лишь как «точка рандеву» в файловой системе между клиентами и серверами на одном и том же узле. Процесс, выполняемый на одном узле, нe мoжem послать данные через FIFO процессу, выполняемому на другом узле, даже если оба процесса смогут открыть этот канал, доступный обоим узлам через сетевую файловую систему.
Читать дальшеИнтервал:
Закладка: