Пример выполнения лабораторной работы

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

Мы предлагаем реализацию этой модели в одной программе. Функция 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