- Фантастика и фэнтези
- Ироническое фэнтези
- Стимпанк
- Детективная фантастика
- Киберпанк
- Попаданцы
- LitRPG
- Космоопера
- Технофэнтези
- Городская фантастика
- Русское фэнтези
- Юмористическая фантастика
- Космическая фантастика
- Боевое фэнтези
- Эпическая фантастика
- Зарубежная фантастика
- Книги магов
- Мистика
- Разная фантастика
- Разное фэнтези
- Любовное фэнтези
- Романтическая фантастика
- Городское фентези
- Историческая фантастика
- Историческое фэнтези
- Социально-философская фантастика
- Сказочная фантастика
- Иностранное фэнтези
- Романтическое фэнтези
- Ироническая фантастика
- Ужасы и Мистика
- Постапокалипсис
- Героическая фантастика
- Фэнтези
- Научная Фантастика
- Социально-психологическая
- Альтернативная история
- Боевая фантастика
- Разная литература
- Пословицы, поговорки
- Шахматы
- Визуальные искусства
- Фанфик
- Авто и ПДД
- Военное
- Отраслевые издания
- Гиды, путеводители
- Зарубежная образовательная литература
- Военная история
- Цитаты из афоризмов
- Современная литература
- Великолепные истории
- Начинающие авторы
- Современная зарубежная литература
- Боевые искусства
- Изобразительное искусство, фотография
- Кино
- Литература 19 века
- Недвижимость
- Музыка, музыканты
- Готические новеллы
- Истории из жизни
- Культура и искусство
- Спецслужбы
- Зарубежная прикладная литература
- Музыка, танцы
- Подростковая литература
- Прочее
- Военная техника, оружие
- Газеты и журналы
- Периодические издания
- Домоводство, Дом и семья
- Любовные романы
- Экономическая литература
- Научные и научно-популярные книги
- О животных
- Биохимия
- Культурология
- Юриспруденция
- Политика
- Психотерапия
- Языкознание
- Научпоп
- Медицина
- Психология, личное
- Радиотехника
- Филология
- Педагогика
- Образовательная литература
- Деловая литература
- Физика
- Науки: разное
- Химия
- Воспитание детей, педагогика
- Детская психология
- Зарубежная публицистика
- Биология
- Архитектура
- Зарубежная психология
- Науки о космосе
- Социология
- Математика
- География
- Беременность, ожидание детей
- Литературоведение
- Транспорт, военная техника
- Обществознание
- Зоология
- География
- Альтернативная медицина
- Иностранные языки
- Ветеринария
- Рефераты
- Астрология
- Биофизика
- Экология
- История Европы
- Учебники
- Шпаргалки
- Государство и право
- Ботаника
- Религиоведение
- История
- Техническая литература
- Прочая научная литература
- Психология
- Детективы и Триллеры
- Справочная литература
- Проза
- Новелла
- Юмор
- Проза
- Историческая проза
- Русская современная проза
- Советская классическая проза
- Повести
- Очерки
- Эссе
- Рассказы
- Зарубежная современная проза
- Сентиментальная проза
- Зарубежная классика
- Афоризмы
- Эпистолярная проза
- Феерия
- Семейный роман/Семейная сага
- Разное
- Антисоветская литература
- Магический реализм
- Русская классическая проза
- Современная проза
- О войне
- Контркультура
- Классическая проза
- Поэзия, Драматургия
- Детская литература
- Детская фантастика
- Детские остросюжетные
- Детские стихи
- Детские приключения
- Внеклассное чтение
- Учебная литература
- Зарубежные детские книги
- Бизнес
- Загадки
- Книги для подростков
- Школьные учебники
- Детский фольклор
- Книги для дошкольников
- Детские детективы
- Детская познавательная и развивающая литература
- Буквари
- Прочая детская литература
- Сказка
- Детская проза
- Детская образовательная литература
- Юмор
- Документальные книги
- Бизнес
- Внешнеэкономическая деятельность
- Тайм-менеджмент
- Кадровый менеджмент
- О бизнесе популярно
- Управление, подбор персонала
- Бизнес
- Менеджмент и кадры
- Продажи
- Экономика
- Ценные бумаги и инвестиции
- Малый бизнес
- Делопроизводство, офис
- Корпоративная культура, бизнес
- Банковское дело
- Финансы
- Зарубежная деловая литература
- Ораторское искусство / риторика
- Личные финансы
- Интернет-бизнес
- Поиск работы
- Государственное и муниципальное управление
- Бухучет и аудит
- Работа с клиентами
- Менеджмент
- Краткое содержание
- Личная эффективность
- Переговоры
- Приключения
- Старинная литература
- Религия и духовность
- Компьютеры и Интернет
- Блог
Читаем без скачивания UNIX: взаимодействие процессов - Уильям Стивенс
Шрифт:
Интервал:
Закладка:
10 for (;;) {
11 /* считывание полного имени из канала IPC */
12 mesg.mesg_type = 1:
13 if ((n = Mesg_recv(readfd, &mesg)) == 0) {
14 err_msg("pathname missing");
15 continue;
16 }
17 mesg.mesg_data[n] = ' '; /* полное имя */
18 if ((ptr = strchr(mesg.mesg_data, ' ')) == NULL) {
19 err_msg("bogus request: %s", mesg.mesg_data);
20 continue;
21 }
22 *ptr++ =0; /* ptr = полное имя */
23 pid = atol(mesg.mesg_data);
24 mesg.mesg_type = pid; /* для обратных сообщений */
25 if ((fp = fopen(ptr, "r")) == NULL) {
26 /* 4error: must tell client */
27 snprintf(mesg.mesg_data + n, sizeof(mesg.mesg_data) – n,
28 ": can't open. %sn", strerror(errno));
29 mesg.mesg_len – strlen(ptr);
30 memmove(mesg.mesg_data, ptr, mesg.mesg_len);
31 Mesg_send(writefd, &mesg);
32 } else {
33 /* файл открыт, копируем клиенту */
34 while (Fgets(mesg.mesg_data, MAXMESGDATA, fp) != NULL) {
35 mesg.mesg_len = strlen(mesg.mesg_data);
36 Mesg_send(writefd, &mesg);
37 }
38 Fclose(fp);
39 }
40 /* сообщение нулевой длины заканчивает связь */
41 mesg.mesg_len = 0;
42 Mesg_send(writefd, &mesg);
43 }
44 }
Листинг 6.14. Функция main клиента//svmsgmpx1q/client_main.c
1 #include "svmsg.h"
2 void client(int, int);
3 int
4 main(int argc, char **argv)
5 {
6 int msqid;
7 /* сервер должен был создать очередь */
8 msqid = Msgget(MQ_KEY1, 0);
9 client(msqid, msqid); /* одна очередь в обе стороны */
10 exit(0);
11 }
Листинг 6.15. Функция client//svmsgmpx1q/client.с
1 #include "mesg.h"
2 void
3 client(int readfd, int writefd)
4 {
5 size_t len;
6 ssize_t n;
7 char *ptr;
8 struct mymesg mesg;
9 /* инициализируем буфер идентификатором процесса и пробелом */
10 snprintf(mesg.mesg_data, MAXMESGDATA. "%ld ", (long) getpid());
11 len = strlen(mesg.mesg_data);
12 ptr = mesg.mesg_data + len;
13 /* считываем полное имя файла */
14 Fgets(ptr, MAXMESGDATA – len, stdin);
15 len = strlen(mesg.mesg_data);
16 if (mesg.mesg_data[len-1] == 'n')
17 len--; /* удаляем перевод строки fgets() */
18 mesg.mesg_len = len;
19 mesg.mesg_type = 1;
20 /* записываем PID и имя файла в канал IPC */
21 Mesg_send(writefd, &mesg);
22 /* считываем из канала IPC, записываем в stdout */
23 mesg.mesg_type = getpid();
24 while ((n = Mesg_recv(readfd, &mesg)) > 0)
25 Write(STDOUT_FILENO, mesg.mesg_data, n);
26 }
Пример: одна очередь для каждого клиента
Изменим теперь предыдущий пример таким образом, чтобы все запросы клиентов передавались по одной очереди, но для отправки ответов использовалась бы отдельная очередь для каждого клиента. На рис. 6.3 изображена схема такого приложения.
Рис. 6.3. Одна очередь для сервера и по одной для каждого клиента
Ключ очереди сервера должен быть известен клиентам, а сами клиенты создают свои очереди с ключом IPC_PRIVATE. Вместо передачи серверу идентификатора процесса клиенты сообщают ему идентификатор своей очереди, в которую сервер направляет свой ответ. Этот сервер является параллельным: для каждого нового клиента порождается отдельный процесс.
ПРИМЕЧАНИЕ
При такой схеме может возникнуть проблема в случае «гибели» клиента, потому что тогда сообщения останутся в его очереди навсегда (по крайней мере до перезагрузки ядра или явного удаления очереди другим процессом).
Нижеследующие заголовочные файлы и функции не претерпевают изменений по сравнению с предыдущими версиями:
■ mesg.h (листинг 4.12);
■ svmsg.h (листинг 6.7);
■ функция main сервера (листинг 6.12);
■ функция mesg_send (листинг 4.13).
Функция main клиента приведена в листинге 6.16; она слегка изменилась по сравнению с листингом 6.14. Мы открываем очередь сервера с известным ключом (MQ_KEY1) и создаем нашу собственную очередь с ключом IPC_PRIVATE. Два идентификатора этих очередей становятся аргументами функции client (листинг 6.17). После завершения работы клиента его персональная очередь удаляется.
Листинг 6.16. Функция main клиента//svmsgmpxnq/client_main.с
1 #include "svmsg.h"
2 void client(int, int);
3 int
4 main(int argc, char **argv)
5 {
6 int readid, writeid;
7 /* сервер должен создать свою очередь */
8 writeid = Msgget(MQ_KEY1, 0);
9 /* мы создаем свою собственную очередь */
10 readid = Msgget(IPC_PRIVATE, SVMSG_MODE | IPC_CREAT);
11 client(readid, writeid);
12 /* и удаляем нашу собственную очередь */
13 Msgctl(readid, IPC_RMID, NULL);
14 exit(0);
15 }
Листинг 6.17. Функция client//svmsgmpxnq/client.с
1 #include "mesg.h"
2 void
3 client(int readid, int writeid)
4 {
5 size_t len;
6 ssize_t n;
7 char *ptr;
8 struct mymesg mesg;
9 /* инициализируем буфер идентификатором очереди и пробелом */
10 snprintf(mesg.mesg_data, MAXMESGDATA, "%d ", readid);
11 len = strlen(mesg.mesg_data);
12 ptr = mesg.mesg_data + len;
13 /* считываем имя файла */
14 Fgets(ptr, MAXMESGDATA – len, stdin);
15 len = strlen(mesg.mesg_data);
16 if (mesg.mesg_data[len-1] == 'n')
17 len--; /* удаляем перевод строки fgets() */
18 mesg.mesg_len = len;
19 mesg.mesg_type = 1;
20 /* отправляем идентификатор очереди и имя файла серверу */
21 Mesg_send(writeid, &mesg);
22 /* считываем ответ из нашей очереди и записываем его в stdout */
23 while ((n = Mesg_recv(readid, &mesg)) > 0)
24 Write(STDOUT_FILENO, mesg.mesg_data, n);
25 }
В листинге 6.17 приведен текст функции client. Эта функция практически идентична функции из листинга 6.15, но вместо передачи идентификатора процесса клиента на сервер направляется идентификатор очереди клиента. Тип сообщения в структуре mesg остается равным 1, поскольку это значение устанавливается для сообщений, передаваемых в обе стороны.
В листинге 6.19 приведена функция server. Главное отличие от листинга 6.13 в том, что эта функция представляет собой бесконечный цикл, в котором для каждого нового клиента вызывается fork.
Установка обработчика сигнала для SIGCHLD10 Поскольку для каждого клиента порождается отдельный процесс, нужно позаботиться о процессах-зомби. В разделах 5.9 и 5.10 [24] об этом говорится подробно. Здесь мы просто установим обработчик для сигнала SIGCHLD, и наша функция sig_chld (листинг 6.18) будет вызываться при завершении работы дочернего процесса.
12-18 Породивший процесс сервера блокируется в вызове mesg_recv, ожидая появления сообщения от очередного клиента.
25-45 Вызовом fork порождается новый процесс, который производит попытку открыть запрошенный файл и отправляет клиенту либо сообщение об ошибке, либо содержимое файла. Мы преднамеренно поместили вызов fopen в дочерний процесс, а не в родительский, поскольку если файл находится в удаленной файловой системе, его открытие может занять довольно много времени в случае наличия проблем с сетью.
Функция-обработчик для SIGCHLD приведена в листинге 6.18. Она скопирована с листинга 5.11 [24].
Листинг 6.18. Обработчик сигнала SIGCHLD, вызывающий waitpid//svmsgmpxnq/sigchldwaitpid.с
1 #include "unpipc.h"
2 void
3 sig_chld(int signo)
4 {
5 pid_t pid;
6 int stat;
7 while ((pid = waitpid(-1, &stat, WNOHANG)) > 0);
8 return;
9 }
Каждый раз при вызове обработчика происходит циклический вызов 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.c