Пример выполнения лабораторной работы |
В этой работе мы моделируем следующую "легенду". В засушливый период русла рек саванны пересыхают и превращаются в цепочки луж, воды в которых становится все меньше и меньше. Стадо Слонов движется вдоль бывшего русла реки в поисках таких луж. Когда стадо находит лужу, один из Слонов остается возле лужи и пьет, время водопоя прямо пропорционально емкости лужи и обратно пропорционально возрасту Слона. Если размер имеющейся в луже порции воды превышает потребность Слона, то Слон не пьет сверх своей потребности. Но чаще всего количества оставшейся в луже воды не бывает достаточно, чтобы удовлетворить потребность. В таком случае Слон, выпив всю доставшуюся ему воду, догоняет стадо и продолжает поиск вместе со всеми.
Мы предлагаем реализацию этой модели в одной программе. Функция 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 |
|