Пример выполнения лабораторной работы |
В этой работе мы моделируем следующую "легенду". В засушливый период русла рек саванны пересыхают и превращаются в цепочки луж, воды в которых становится все меньше и меньше. Стадо Слонов движется вдоль бывшего русла реки в поисках таких луж. Когда стадо находит лужу, один из Слонов остается возле лужи и пьет, время водопоя прямо пропорционально емкости лужи и обратно пропорционально возрасту Слона. Если размер имеющейся в луже порции воды превышает потребность Слона, то Слон не пьет сверх своей потребности. Но чаще всего количества оставшейся в луже воды не бывает достаточно, чтобы удовлетворить потребность. В таком случае Слон, выпив всю доставшуюся ему воду, догоняет стадо и продолжает поиск вместе со всеми.
Мы предлагаем реализацию этой модели в одной программе. Функция main этой программы реализует деятельность Ганеши - "коллективного разума" стада. Функция elephant - деятельность Слона.
Ганеша начинает свою деятельность с того, что создает два неименованных канала. По одному из них (выходному) Ганеша будет передавать стаду порции воды, по другому (входному) - принимать от Слонов доклады об окончании водопоя. Затем Ганеша порождает дочерние процессы. В дочернем процессе вызывается функция elephant, в родительском - продолжает выполнение функция main.
Породив слонов, Ганеша подсчитывает общую потребность стада в воде с избытком в 20%. Далее Ганеша через случайные интервалы времени "находит" порции воды случайной емкости и передает их в свой выходной канал. Он повторяет эти действия до тех пор, пока объем переданных в канал порций не превысит суммарную потребность стада. Обеспечив Слонов водой Ганеша "погружается в спячку" но 30 сек.
Выходной канал Ганеши является входным каналом для всех Слонов и наоборот. Процесс-Слон, запустившись, определяет свою потребность в воде (она пропорциональна его весу) и пытается читать свой входной канал. Если в канале нет данных, то Слон переводится в состояние ожидания до появления данных в канале. Поскольку несколько процессов-Слонов пытаются одновременно читать данные из одного выходного канала Ганеши, появившиеся в канале данные достанутся только одному из ожидающих процессов, который их прочитает и начнет обрабатывать ("пить"), остальные же ожидающие процессы будут ждать следующей порции данных в канале.
Процесс-Слон, прочитавший из канала порцию данных, при помощи функции занятого ожидания a0wait имитирует ее потребление, после чего уменьшает свою потребность на размер употребленной порции. Если потребность не свелась к 0, Слон опять пытается читать данные из канала.
Если потребность Слона свелась к 0, Слон выводит в свой выходной канал свое имя - как доклад об окончании водопоя, закрывает свои каналы и завершается.
По истечении 30-секундного интервала Ганеша проверяет доклады Слонов. Поскольку все выходные каналы Слонов являются одним входным каналом Ганеши, все доклады Слонов Ганеше попадают в этот входной канал. Однако к моменту, когда Ганеша начинает проверять доклады Слонов в своем входном канале, еще не все Слоны могут прислать доклады. Поэтому проверка входного канала в цикле по счетчику Слонов может привести к тому, что процесс-Ганеша перейдет в ожидание при попытке чтения очередного доклада из пустого канала. Для того, чтобы избежать такой ситуации, применен следующий искусственный прием. На чтение всех докладов из своего входного канала Ганеше отводится 1 сек (этого более, чем достаточно). Перед началом чтения Ганеша выполняет системный вызов alarm, программируя им посылку самому себе сигнала SIGALRM через 1 сек. Кроме того, процесс запоминает свое состояние (системным вызовом setjmp) перед началом цикла опроса входного канала. Цикл опроса входного канала выполняется, пока переменная alrmFlag не будет установлена в 1 (ее начальное значение - 0). Прочитав все содержимое входного канала, процесс-Ганеша перейдет в состояние ожидания на попытке очередного чтения. Из ожидания его выведет сигнал SIGALRM. В обработчике этого сигнала устанавливается в 1 переменная alrmFlag и управление передается (системным вызовом longjmp) на точку перед циклом, состояние которой было запомнено ранее. Цикл опроса теперь выполняться не будет, Ганеша закрывает свои каналы и принудительно прекращает те процессы, доклады от которых не поступили.
Кодовый модуль ganesha3.c реализующий эту модель, показан ниже. Мы реализовали оба типа процессов в одной программе, чтобы облегчить передачу дескрипторов каналов от родительского процесса к дочернему.
Готовый проект с откомпилированным кодом вы можете скачать здесь:)
/**********************************************/
/* Пример для работы N11 */
/**********************************************/
/* НЕИМЕНОВАННЫЕ ПРОГРАММНЫЕ КАНАЛЫ */
/**********************************************/
/* Монитор Слонов - файл ganesha3.c */
/**********************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <signal.h>
#include <sys/resource.h>
#include <setjmp.h>
#define MESS_LEN 100
#include "../common/curtime.h"
#include "../common/wait.h"
#include "../common/elephant.h"
/*static*/ int ne; // параметр циклов
/*static*/ meleph mel[NE]; // управляющая информация о Слонах
int cnt; // число запущенных Слонов
int alrmFlag; /* флаг поступления сигнала */
int fd1[2], fd2[2]; /* дескрипторы каналов */
char mess[MESS_LEN];
jmp_buf jb; /* буфер состояния для longjmp */
/**********************************************/
/* Эта функция выполняется в дочернем процессе и моделирует работу Слона */
/* Параметр функции - структура личных данных Слона */
void f_elephant(elephant *e)
{
long need, /* потребность Слона в воде */
myport; /* размер полученной порции */
/* закрытие лишних канальных дескрипторов */
close(fd2[0]); close(fd1[1]);
need=e->weight*500; /* вычисление потребности */
printf("%s Слон %s приступил. Потребность - %ld, приоритет - %d\n",
curtime(),e->name,need,getpriority(PRIO_PROCESS,getpid()));
/* цикл, пока не будет удовлетворена потребность */
while (need>0)
{
/* ожидание и получение порции */
read(fd1[0],&myport,sizeof(long));
if (myport>need) myport=need;
/* водопой */
a0wait((int)(myport/e->age*10));
/* уменьшение потребности */
need-=myport;
printf("%s Слон %s выпил %ld, остаток потребности - %ld\n",
curtime(),e->name,myport,need);
}
printf("%s Слон %s водопой закончил\n",curtime(),e->name);
/* вывод в канал доклада о завершении и завершение */
write(fd2[1],e->name,strlen(e->name)+1);
close(fd2[1]);
close(fd1[0]);
exit(0);
}
/**********************************************/
/* обработчик сигнала SIGALRM в родительском процессе */
void alrmH(int s)
{
printf("%s ALARM!\n",curtime());
alrmFlag=1; /* установка флага */
longjmp(jb,1); /* "длинный" переход на начало цикла опроса канала в функции main */
}
/**********************************************/
/* Функция main моделирует работу Ганеши */
main()
{
pid_t pw; /* PID запущенного процесса */
int s, i;
long port; /* размер очередной порции */
long total; /* общая потребность стада */
pipe(fd1);
pipe(fd2); /* создание каналов */
/* цикл запуска Слонов */
for (ne=0; ne<NE; ne++)
{
/* запись личных данных в управляющую информацию */
mel[ne].el=&ee[ne];
/* порождение процесса */
pw=fork();
if (pw<0)
{
printf("pw=%d ne=%d\n",pw,ne);
exit(0);
}
if (!pw)
/* в дочернем процессе запускается функция elephant */
f_elephant(mel[ne].el);
else
{
/* запоминание характеристик процесса-Слона */
mel[ne].status=-1;
mel[ne].prty=(int)(10.*rand()/RAND_MAX);
setpriority(PRIO_PROCESS,pw,mel[ne].prty);
mel[ne].chpid=pw;
}
}
/* закрытие лишних канальных дескрипторов */
close(fd2[1]); close(fd1[0]);
/* инициализация генератора случайных чисел */
srand(time(NULL));
/* вычисление общей потребности стада с запасом 20% */
for(total=ne=0; ne<NE; ne++)
total+=mel[ne].el->weight*1000;
total=1.2*total;
/* пауза - чтобы Слоны успели запуститься */
sleep(1);
/* цикл, пока не буден удовлетворена потребность стада */
while(total>0)
{
/* получение очередной порции */
port=1000.*rand()/RAND_MAX;
/* передача порции в канал */
write(fd1[1],&port,sizeof(long));
/* уменьшение общей потребности */
total-=port;
/* задержка на поиск следующей порции */
usleep((int)(2000.*rand()/RAND_MAX));
}
sleep(10);
printf("%s ВРЕМЯ ОЖИДАНИЯ ИСТЕКЛО\n",curtime());
/* отводится 1 сек на прием докладов от Слонов */
signal(SIGALRM,alrmH);
alarm(1);
alrmFlag=0;
/* запоминание точки возврата из обработчика сигнала SIGALRM */
setjmp(jb);
/* цикл, пока не поступит SIGALRM */
while (!alrmFlag)
{
/* чтение из канала строки с именем Слона */
for (i=0;;i++)
{
while(!read(fd2[0],mess+i,1));
if (mess[i]==0) break;
}
/* поиск Слона по имени */
for (ne=0; ne<NE; ne++)
if (!strcmp(mess,mel[ne].el->name)) break;
printf("%s Завершение слона %s зафиксировано\n",curtime(),mess);
/* отметка о завершении */
mel[ne].status=1;
}
/* закрытие каналов */
close(fd2[0]); close(fd1[1]);
/* перебор списка Слонов */
for (ne=0; ne<NE; ne++)
{
/* тем, кто не доложил о завершении посылается SIGKILL */
if (mel[ne].status!=1) kill(mel[ne].chpid,SIGKILL);
/* Ганеша убеждается в завершении Слона */
waitpid(mel[ne].chpid,&s,0);
/* Сообщение о гибели, если Слон закончился с ненулевым кодом */
if (s) printf("%s Зафиксирована гибель слона %s %d\n",
curtime(),mel[ne].el->name,s);
}
}
|
Ниже приводится пример выполнения этой модели
08:47:09.365 Слон Maya приступил. Потребность - 2149, приоритет - 0 08:47:09.370 Слон Tandy приступил. Потребность - 849, приоритет - 8 08:47:09.373 Слон Assam приступил. Потребность - 2899, приоритет - 7 08:47:09.368 Слон Aun приступил. Потребность - 1100, приоритет - 3 08:47:09.380 Слон Hao приступил. Потребность - 1600, приоритет - 1 08:47:09.383 Слон BakZap приступил. Потребность - 2250, приоритет - 9 08:47:09.378 Слон Hathy приступил. Потребность - 3450, приоритет - 0 08:47:09.388 Слон Kitty приступил. Потребность - 1500, приоритет - 7 08:47:10.550 Слон Hathy выпил 39, остаток потребности - 3411 08:47:12.038 Слон Hathy выпил 216, остаток потребности - 3195 08:47:12.739 Слон Hathy выпил 131, остаток потребности - 3064 08:47:13.452 Слон Maya выпил 279, остаток потребности - 1870 08:47:16.563 Слон Assam выпил 537, остаток потребности - 2362 08:47:17.582 Слон Assam выпил 146, остаток потребности - 2216 08:47:19.213 Слон Hathy выпил 708, остаток потребности - 2356 08:47:21.005 Слон Kitty выпил 201, остаток потребности - 1299 08:47:21.435 Слон BakZap выпил 914, остаток потребности - 1336 08:47:23.273 Слон Hathy выпил 465, остаток потребности - 1891 08:47:23.677 Слон Maya выпил 810, остаток потребности - 1060 08:47:27.759 Слон Maya выпил 340, остаток потребности - 720 08:47:27.812 Слон Assam выпил 815, остаток потребности - 1401 08:47:28.054 Слон Hathy выпил 568, остаток потребности - 1323 08:47:29.242 Слон Tandy выпил 108, остаток потребности - 741 08:47:30.978 Слон BakZap выпил 760, остаток потребности - 576 08:47:35.460 Слон Hao выпил 872, остаток потребности - 728 08:47:35.537 Слон Hathy выпил 767, остаток потребности - 556 08:47:36.971 Слон Maya выпил 703, остаток потребности - 17 08:47:36.971 Слон Maya выпил 17, остаток потребности - 0 08:47:36.972 Слон Maya водопой закончил 08:47:37.001 Слон Assam выпил 739, остаток потребности - 662 08:47:37.709 Слон BakZap выпил 576, остаток потребности - 0 08:47:37.709 Слон BakZap водопой закончил 08:47:38.702 Слон Assam выпил 238, остаток потребности - 424 08:47:39.731 Слон Hathy выпил 556, остаток потребности - 0 08:47:39.731 Слон Hathy водопой закончил 08:47:40.204 Слон Assam выпил 223, остаток потребности - 201 08:47:41.523 Слон Assam выпил 201, остаток потребности - 0 08:47:41.523 Слон Assam водопой закончил 08:47:41.900 ВРЕМЯ ОЖИДАНИЯ ИСТЕКЛО 08:47:41.901 Завершение слона Maya зафиксировано 08:47:41.902 Завершение слона BakZap зафиксировано 08:47:41.902 Завершение слона Hathy зафиксировано 08:47:41.902 Завершение слона Assam зафиксировано 08:47:42.900 ALARM! 08:47:43.000 Зафиксирована гибель слона Tandy 9 08:47:43.050 Зафиксирована гибель слона Aun 9 08:47:43.090 Зафиксирована гибель слона Hao 9 08:47:43.091 Зафиксирована гибель слона Kitty 9 |
© life-prog.ru |