Смекни!
smekni.com

Теория вычислительных процессов и структур (стр. 8 из 8)

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


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

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

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

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

Теоретическая часть

Для простых приложений применение неблокирующих операций чтения и записи работает прекрасно. Для работы с множеством каналов одновременно существует другое решение, которое заключается в использовании системного вызова select.

Возможна ситуация, когда родительский процесс выступает в качестве серверного процесса и может иметь произвольное число связанных с ним клиентских (дочерних) процессов, как показано на рис. 6.1.

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

В этом случае серверный процесс должен как-то справляться с ситуацией, когда одновременно в нескольких каналах может находиться информация, ожидающая обработки. Кроме того, если ни в одном из каналов нет ожидающих данных, то может иметь смысл приостановить работу серверного процесса до их появления, а не опрашивать постоянно каналы. Если информация поступает более чем по одному каналу, то серверный процесс должен знать обо всех таких каналах для того, чтобы работать с ними в правильном порядке (например, согласно их приоритету).

Это можно сделать при помощи системного вызова select (существует также аналогичный вызов poll). Системный вызов select используется не только для каналов, но и для обычных файлов, терминальных устройств, именованных каналов и сокетов. Системный вызов select показывает, какие дескрипторы файлов из заданных наборов готовы для чтения, записи или ожидают обработки ошибок. Иногда серверный процесс не должен совсем прекращать работу, даже если не происходит никаких событий, поэтому в вызове select также можно задать предельное время ожидания. Описание данного вызова:

#include <sys/time.h>

int select (int nfds, fd_set *readfds, fd_set *writefds,

fd_set *errorfds, struct timeval *timeout);

Первый параметр nfds задает число дескрипторов файлов, которые могут представлять интерес для сервера. Программист может определять это значение самостоятельно или воспользоваться постоянной FD_SETSIZE, которая определена в файле <sys/time.h>. Значение постоянной равно максимальному числу дескрипторов файлов, которые могут быть использованы вызовом select.

Второй, третий и четвертый параметры вызова являются указателями на битовые маски, в которых каждый бит соответствует дескриптору файла. Если бит включен, то это обозначает интерес к соответствующему дескриптору файла. Набор readfds определяет дескрипторы, для которых сервер ожидает возможности чтения; набор writefds – дескрипторы, для которых сервер ожидает возможности выполнить запись; набор errorfds – дескрипторы, для которых сервер ожидает появление ошибки или исключительной ситуации. Так как работа с битами довольно неприятна и приводит к немобильности программ, существуют абстрактный тип данных fd_set, а также макросы или функции для работы с объектами этого типа:

#include <sys/time.h>

/*Инициализация битовой маски, на которую указывает 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 <sys/time.h>

#include <sys/types.h>

#include <fcntl.h>

...

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 <sys/time.h>

struct timeval {

long tv_sec; /*Секунды*/

long tv_usec; /*и микросекунды*/

};

Если указатель является нулевым, как в этом примере, то вызов select будет заблокирован, пока не произойдет “интересующее” процесс событие. Если в этой структуре задано нулевое время, то вызов завершится немедленно. Если структура содержит ненулевое значение, то возврат из вызова произойдет через заданное время, когда файловые дескрипторы неактивны.

Возвращаемое вызовом select значение равно -1 в случае ошибки, нулю – после истечения временного интервала или целому числу, равному числу «интересующих» программу дескрипторов файлов. Необходимо сохранять копию исходных масок.

Пример, в котором используются три канала, связанные с тремя дочерними процессами. Родительский процесс должен отслеживать стандартный ввод:

#include <sys/time.h>

#include <sys/wait.h>

#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&bsol;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&bsol;n”, i);

printf (“MSG=%s&bsol;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. На двух машинах запустить процессы и организовать между ними взаимодействие посредством канала. Один из процессов является главным, а второй подчинённым. Главный процесс может инициировать завершение подчинённого процесса.

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

Работа с использованием неименованных каналов

Цель работы - изучение работы системы “производители-потребители” с использованием неименованных каналов.

Порядок выполнения работы

Смоделировать посредством неименованного канала работу системы «производители-потребители». Создать структуру:


Производители посылают сообщения переменной длины, потребители читают эти сообщения. При записи и чтении данных в канал решить задачу взаимного исключения. Формат порции записи


Список литературы

Хэвиленд К., Грэй Д., Салама Б. Системное программирование в UNIX: Руководство программиста по разработке ПО. – М.: ДМК “Пресс”, 2000.

2. WWW ресурс www.opennet.ru/