Уильям Стивенс - UNIX: взаимодействие процессов
- Название:UNIX: взаимодействие процессов
- Автор:
- Жанр:
- Издательство:Питер
- Год:2003
- Город:Санкт-Петербург
- ISBN:5-318-00534-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: взаимодействие процессов краткое содержание
Книга написана известным экспертом по операционной системе UNIX и посвящена описанию одной из форм межпроцессного взаимодействия, IPC, с использованием которой создается большинство сложных программ. В ней описываются четыре возможности разделения решаемых задач между несколькими процессами или потоками одного процесса: передача сообщений, синхронизация, разделяемая память, удаленный вызов процедур.
Книга содержит большое количество иллюстрирующих примеров и может использоваться как учебник по IPC, и как справочник для опытных программистов.
UNIX: взаимодействие процессов - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Здесь pathname — обычное для Unix полное имя файла, которое и будет именем FIFO.
Аргумент mode указывает битовую маску разрешений доступа к файлу, аналогично второму аргументу команды open. В табл. 2.3 приведены шесть констант, определенных в заголовке . Эти константы могут использоваться для задания разрешений доступа и к FIFO.
Функция mkfifo действует как open, вызванная с аргументом O_CREAT | O_EXCL. Это означает, что создается новый канал FIFO или возвращается ошибка EEXIST, в случае если канал с заданным полным именем уже существует. Если не требуется создавать новый канал, вызывайте open вместо mkfifo. Для открытия существующего канала или создания нового в том случае, если его еще не существует, вызовите mkfifo, проверьте, не возвращена ли ошибка EEXIST, и если такое случится, вызовите функцию open.
Команда mkfifо также создает канал FIFO. Ею можно пользоваться в сценариях интерпретатора или из командной строки.
После создания канал FIFO должен быть открыт на чтение или запись с помощью либо функции open, либо одной из стандартных функций открытия файлов из библиотеки ввода-вывода (например, fopen). FIFO может быть открыт либо только на чтение, либо только на запись. Нельзя открывать канал на чтение и запись, поскольку именованные каналы могут быть только односторонними.
При записи в программный канал или канал FIFO вызовом write данные всегда добавляются к уже имеющимся, а вызов read считывает данные, помещенные в программный канал или FIFO первыми. При вызове функции lseek для программного канала или FIFO будет возвращена ошибка ESPIPE.
Пример
Переделаем программу, приведенную в листинге 4.1, таким образом, чтобы использовать два канала FIFO вместо двух программных каналов. Функции client и server останутся прежними; отличия появятся только в функции main, новый текст которой приведен в листинге 4.6.
//pipe/mainfifo.c
1 #include "unpipc.h"
2 #define FIFO1 "/tmp/fifo.1"
3 #define FIFO2 "/tmp/fifo.2"
4 void client(int, int), server(int. int);
5 int
6 main(int argc, char **argv)
7 {
8 int readfd, writefd;
9 pid_t childpid;
10 /* создание двух FIFO, если существуют – OK */
11 if ((mkfifo(FIF01, FILE_MODE) < 0) && (errno != EEXIST))
12 err_sys("can't create %s", FIF01);
13 if ((mkfifo(FIF02, FILE_MODE) < 0) && (errno != EEXIST)) {
14 unlink(FIF01);
15 err_sys("can't create %s", FIF02);
16 }
17 if ((childpid = Fork()) == 0) { /* child */
18 readfd = Open(FIF01, O_RDONLY, 0);
19 writefd = Open(FIF02, O_WRONLY, 0);
20 server(readfd, writefd);
21 exit(0);
22 }
23 /* родительский процесс */
24 writefd = Open(FIF01, O_WRONLY, 0);
25 readfd = Open(FIF02, O_RDONLY, 0);
26 client(readfd, writefd);
27 waitpid(childpid, NULL, 0); /* ожидание завершения дочернего процесса */
28 Close(readfd):
29 Close(writefd);
30 Unlink(FIF01);
31 Unlink(FIF02);
32 exit(0);
33 }
10-16 В файловой системе в каталоге /tmp создается два канала. Если какой-либо из них уже существует — ничего страшного. Константа FILE_MODE определена в нашем заголовке unpiрс.h (листинг В.1) как
#define FILEMODE(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
/* разрешения по умолчанию для вновь создаваемых файлов */
При этом владельцу файла разрешается чтение и запись в него, а группе и прочим пользователям — только чтение. Эти биты разрешений накладываются на маску режима доступа создаваемых файлов (file mode creation mask) процесса.
17-27 Далее происходит вызов fork, дочерний процесс вызывает функцию server (листинг 4.3), а родительский процесс вызывает функцию client (листинг 4.2). Перед вызовом этих функций родительский процесс открывает первый канал на запись, а второй на чтение, в то время как дочерний процесс открывает первый канал на чтение, а второй — на запись. Картина аналогична примеру с каналами и иллюстрируется рис. 4.11.
Рис. 4.11. Приложение клиент-сервер, использующее две очереди
Изменения по сравнению с примером, в которым использовались программные каналы, следующие:
■ Для создания и открытия программного канала требуется только один вызов — pipe. Для создания и открытия FIFO требуется вызов mkfifo и последующий вызов open.
■ Программный канал автоматически исчезает после того, как будет закрыт последним использующим его процессом. Канал FIFO удаляется из файловой системы только после вызова unlink. Польза от лишнего вызова, необходимого для создания FIFO, следующая: канал FIFO получает имя в файловой системе, что позволяет одному процессу создать такой канал, а другому открыть его, даже если последний не является родственным первому. С программными каналами это неосуществимо.
В программах, некорректно использующих каналы FIFO, могут возникать неочевидные проблемы. Рассмотрим, например, листинг 4.6: если поменять порядок двух вызовов функции open в породившем процессе, программа перестанет работать. Причина в том, что чтение из FIFO блокирует процесс, если канал еще не открыт на запись каким-либо другим процессом. Действительно, если мы меняем порядок вызовов open в породившем процессе, и породивший, и порожденный процессы открывают канал на чтение, притом что на запись он еще не открыт, так что оба процесса блокируются. Такая ситуация называется блокированием, или зависанием (deadlock). Она будет рассмотрена подробно в следующем разделе.
Пример: неродственные клиент и сервер
В листинге 4.6 клиент и сервер все еще являлись родственными процессами. Переделаем этот пример так, чтобы родство между ними отсутствовало. В листинге 4.7 приведен текст программы-сервера. Текст практически идентичен той части программы из листинга 4.6, которая относилась к серверу.
Содержимое заголовка fifо.h приведено в листинге 4.8. Этот файл определяет имена двух FIFO, которые должны быть известны как клиенту, так и серверу.
В листинге 4.9 приведен текст программы-клиента, которая не слишком отличается от части программы из листинга 4.6, относящейся к клиенту. Обратите внимание, что именно клиент, а не сервер удаляет канал FIFO по завершении работы, потому что последние операции с этим каналом выполняются им.
//pipe/server_main.c
1 #include "fifo.h"
2 void server(int, int);
3 int
4 main(int argc, char **argv)
5 {
6 int readfd, writefd;
7 /* создание двух FIFO. OK, если они существуют */
8 if ((mkfifo(FIF01, FILE_MODE) < 0) && (errno != EEXIST))
9 err_sys("can't create %s", FIF01);
10 if ((mkfifo(FIF02, FILE MODE) < 0) && (errno != EEXIST)) {
11 unlink(FIF01);
12 err_sys("can't create %s", FIF02);
13 }
14 readfd = Open(FIF01, O_RDONLY, 0);
15 writefd = Open(FIFO2, O_WRONLY, 0);
16 server(readfd, writefd);
17 exit(0);
18 }
//pipe/fifo.h
Интервал:
Закладка: