1. Основные команды ос unix Вход в систему и выход



бет10/11
Дата13.10.2022
өлшемі88,89 Kb.
#42873
1   2   3   4   5   6   7   8   9   10   11
Байланысты:
Лабораторные работы ТОВП (1)

p[1]
read()
Родительский процесс
p[0]

Рис. 5.3. Третий пример работы с каналами




Порядок выполнения работы
1. Изучить теоретическую часть лабораторной работы.
2. Организовать взаимодействие процессов следующей структуры:
Процесс «ответ»
Процесс
«вопрос»(1)
Процесс
«вопрос»(2)
Процесс
«вопрос»(N)

Процессы «вопрос»(ы) посылают запросы процессу «ответ» по неименованным каналам и получают по ним ответы. Должны быть предусмотрены типы ответов, которые инициируют завершение процессов «вопрос», а также должны быть вопросы, которые инициируют порождение новых процессов.


3. Организовать взаимодействие процессов следующей структуры:

Процесс «управление»


Процесс
«работник»1
Процесс
«работник»2
Процесс
«работник»3
Процессы «работники» по неименованным каналам обмениваются между собой данными. Неименованные каналы существуют также между процессом «Управление» и процессами «работниками». Процесс «Управление» инициирует завершение процессов «работников».

Лабораторная работа №6


Работа с несколькими каналами

Цель работы – организация работы процессов с несколькими каналами и их взаимодействие.


Теоретическая часть
Для простых приложений применение неблокирующих операций чтения и записи работает прекрасно. Для работы с множеством каналов одновременно существует другое решение, которое заключается в использовании системного вызова select.
Возможна ситуация, когда родительский процесс выступает в качестве серверного процесса и может иметь произвольное число связанных с ним клиентских (дочерних) процессов, как показано на рис. 6.1.
write()
Дочерний процесс1
p[1]
q[1]
read()
read()
Родительский процесс
p[0]
q[0]
write()
Дочерний процесс2

Рис. 6.1. Клиент/сервер с использованием каналов


В этом случае серверный процесс должен как-то справляться с ситуацией, когда одновременно в нескольких каналах может находиться информация, ожидающая обработки. Кроме того, если ни в одном из каналов нет ожидающих данных, то может иметь смысл приостановить работу серверного процесса до их появления, а не опрашивать постоянно каналы. Если информация поступает более чем по одному каналу, то серверный процесс должен знать обо всех таких каналах для того, чтобы работать с ними в правильном порядке (например, согласно их приоритету).
Это можно сделать при помощи системного вызова select (существует также аналогичный вызов poll). Системный вызов select используется не только для каналов, но и для обычных файлов, терминальных устройств, именованных каналов и сокетов. Системный вызов select показывает, какие дескрипторы файлов из заданных наборов готовы для чтения, записи или ожидают обработки ошибок. Иногда серверный процесс не должен совсем прекращать работу, даже если не происходит никаких событий, поэтому в вызове select также можно задать предельное время ожидания. Описание данного вызова:
#include
int select (int nfds, fd_set *readfds, fd_set *writefds,
fd_set *errorfds, struct timeval *timeout);
Первый параметр nfds задает число дескрипторов файлов, которые могут представлять интерес для сервера. Программист может определять это значение самостоятельно или воспользоваться постоянной FD_SETSIZE, которая определена в файле . Значение постоянной равно максимальному числу дескрипторов файлов, которые могут быть использованы вызовом select.
Второй, третий и четвертый параметры вызова являются указателями на битовые маски, в которых каждый бит соответствует дескриптору файла. Если бит включен, то это обозначает интерес к соответствующему дескриптору файла. Набор readfds определяет дескрипторы, для которых сервер ожидает возможности чтения; набор writefds – дескрипторы, для которых сервер ожидает возможности выполнить запись; набор errorfds – дескрипторы, для которых сервер ожидает появление ошибки или исключительной ситуации. Так как работа с битами довольно неприятна и приводит к немобильности программ, существуют абстрактный тип данных fd_set, а также макросы или функции для работы с объектами этого типа:
#include
/*Инициализация битовой маски, на которую указывает fdset*/
void FD_ZERO (fd_set *fdset);
/*Установка бита fd в маске, на которую указывает fdset*/
void FD_SET (int fd, fd_set *fdset);
/*Установлен ли бит fd в маске, на которую указывает fdset?*/
int FD_ISSET (int fd, fd_set *fdset);
/*Сбросить бит fd в маске, на которую указывает fdset*/
void FD_GLR (int fd, fd_set *fdset);
Следующий пример демонстрирует, как отслеживать состояние двух открытых дескрипторов файлов:
#include
#include
#include
...
int fd1, fd2;
fd_set readset;
fd1 = open (“file1”, O_RDONLY);
fd2 = open (“file2”, O_RDONLY);
FD_ZERO (& readset);
FD_SET (fd1, &readset);
FD_SET (fd2, &readset);
switch (select (5, &readset, NULL, NULL, NULL))
{
/*Обработка ввода*/
}
Пятый параметр вызова select является указателем на следующую структуру timeval:
#include
struct timeval {
long tv_sec; /*Секунды*/
long tv_usec; /*и микросекунды*/
};
Если указатель является нулевым, как в этом примере, то вызов select будет заблокирован, пока не произойдет “интересующее” процесс событие. Если в этой структуре задано нулевое время, то вызов завершится немедленно. Если структура содержит ненулевое значение, то возврат из вызова произойдет через заданное время, когда файловые дескрипторы неактивны.
Возвращаемое вызовом select значение равно -1 в случае ошибки, нулю – после истечения временного интервала или целому числу, равному числу «интересующих» программу дескрипторов файлов. Необходимо сохранять копию исходных масок.
Пример, в котором используются три канала, связанные с тремя дочерними процессами. Родительский процесс должен отслеживать стандартный ввод:
#include
#include
#define MSGSIZE 6
char *msg1 = “hello”;
char *msg2 = “bye”;
void parent (int [] []);
int child (int []);
main()
{
int pip [3] [2];
int i;
/*Создает три канала связи и порождает три процесса*/
for (i = 0; i < 3; i++)
{
if (pipe (pip [i]) == -1)
fatal (“Ошибка вызова pipe”);
switch (fork ()) {
case -1: /*Ошибка*/
fatal (“Ошибка вызова fork”);
case 0: /*Дочерний процесс*/
child (pip [i]);
}
}
parent (pip);
exit (0);
}
/*Родительский процесс ожидает сигнала в трех каналах*/
void parent (int p [3] [2]) /*Код родительского процесса*/
{
char buf [MSGSIZE], ch;
fd_set set, master;
int i;
/*Закрывает все ненужные дескрипторы, открытые для записи*/
for (i = 0; i < 3; i++)
close (p [i] [1]);
/*Задает битовые маски для системного вызова select*/
FD_ZERO (&master);
FD_SET (0, &master);
for (i = 0; i < 3; i++)
FD_SET (p [i] [0], &master);
/*Лимит времени для вызова select не задан, поэтому он будет*/
/*заблокирован, пока не произойдет событие*/
while (set = master, select (p [2] [0] + 1, &set, NULL, NULL, NULL) > 0)
{
/*Нельзя забывать и про стандартный ввод,*/
/* то есть дескриптор файла fd = 0*/
if (FD_ISSET (0, &set))
{
printf (“Из стандартного ввода…”);
read (0, &ch, 1);
printf (“%c\n”, ch);
}
for (i = 0; i < 3; i++)
{
if (FD_ISSET (p [i] [0], &set))
{
if (read (p [i] [0], buf, MSGSIZE) > 0)
{
printf (“Сообщение от потомка %d\n”, i);
printf (“MSG=%s\n”, buf);
}
}
}
/*Если все дочерние процессы прекратили работу,*/
/*то сервер вернется в основную программу*/
if (waitpid (-1, NULL, WNOHANG) == -1)
return;
}
}
int child (int p [2])
{
int count;
close (p [0]);
for (count = 0; count < 2; count++)
{
write (p [1], msg1, MSGSIZE);
/*Пауза в течение случайно выбранного времени*/
sleep (getpid () % 4);
}
/*Посылает последнее сообщение*/
write (p [1], msg2, MSGSIZE);
exit (0);
}
Результат данной программы может быть таким:
Сообщение от потомка 0
MSG=hello
Сообщение от потомка 1
MSG=hello
Сообщение от потомка 2
MSG=hello
d (пользователь нажимает клавишу d, а затем клавишу Return)
Из стандартного ввода d (повторение символа d )
Из стандартного ввода d (повторение символа Return )
Сообщение от потомка 0
MSG=hello
Сообщение от потомка 1
MSG=hello
Сообщение от потомка 2
MSG=hello
Сообщение от потомка 0
MSG=bye
Сообщение от потомка 1
MSG= bye
Сообщение от потомка 2
MSG= bye
Обратите внимание, что в этом примере пользователь нажимает клавишу d, а затем символ перевода строки (Enter или Return), и это отслеживается в стандартном вводе в вызове select.




Достарыңызбен бөлісу:
1   2   3   4   5   6   7   8   9   10   11




©emirsaba.org 2024
әкімшілігінің қараңыз

    Басты бет