Пассивное ожидание обновления условий Linux

Я пытаюсь создать код, который имитирует центр по уходу за ребенком. В этом центре один взрослый может ухаживать за тремя детьми. Это условие должно выполняться постоянно. Взрослые и дети-это процессы, генерируемые случайным образом, а количество детей и взрослых задается в аргументах программы. Ребенок может войти, только если внутри достаточно взрослых, а взрослый может выйти, только если достаточно других взрослых, чтобы заботиться о детях. Если нет, пассивное ожидание должно быть реализовано, пока условие позволяет ребенку / взрослому выйти / войти.

#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/shm.h>

 void load_init_values();
 void handler(int, int, char*);

 pid_t adults, children;
 int adult_max_t, child_max_t, adc, chc, amt, cmt, shm_a_id, shm_c_id;
 int *adults_inside, *children_inside;
 sem_t *adults_sem, *children_sem, *entry;


int main(int argc, char *argv[])
{

 srand(time(NULL));
 setbuf(stdout,NULL);

 adc=atoi(argv[1]);
 chc=atoi(argv[2]);
 adult_max_t=atoi(argv[3]);
 child_max_t=atoi(argv[4]);
 amt=atoi(argv[5]);
 cmt=atoi(argv[6]);

 int pid=0;

 load_init_values();
 adults = fork();

 if (adults == 0)
 {

   for(int i=0; i<=adc-1; i++)
   {
      int adult_t = (random() % (adult_max_t + 1));
      usleep(adult_t*1000);

       adults = fork();

       // Adult process is created here
       if(adults == 0)
       {
        handler(getpid(), amt, "adult");
       }
       else
       {
       }
   }
 }

 else
 {
   children = fork();

   if (children == 0)
   {

     for(int i=0; i<=chc-1; i++)
     {
       int child_t = (random() % (child_max_t + 1));
       usleep(child_t*1000);

       children = fork();

       // Child process is created here
       if(children == 0)
       {
        handler(getpid(), cmt, "child");
        break;
       }
       else
       {
       }
     }
   }

   else
   {
   }
 }
 return 0;
}

void handler(int pid,int maxtime, char* type)
{
 sem_wait(entry);

  printf("%s %i%sn",type,pid," attempting to enter...");

  if(type == "child")
  {
    int child_leave_t = (random() % (maxtime + 1));

    if((*adults_inside) != 0)
    {

     if(((*children_inside)+1)/(*adults_inside) <= 3)
     {
       (*children_inside)++;
       printf("%s %i%sn",type,pid," entered!");

       usleep(child_leave_t*1000);

       printf("%s %i%sn",type,pid," left!");
       (*children_inside)--;
     }

     else
     {
        printf("%s %i%sn",type,pid," can not enter. Waiting...");
     }

    }
    else
    {
        printf("%s %i%sn",type,pid," can not enter. Waiting...");
    }
  }

  else if(type == "adult")
  {

    (*adults_inside)++;
    printf("%s %i%sn",type,pid," entered.");

  }

 sem_post(entry);
}

void load_init_values()
{
 adults_sem = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
 children_sem = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
 entry = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
 shm_a_id = shmget(IPC_PRIVATE, sizeof(int), IPC_CREAT | IPC_EXCL | 0666);
 shm_c_id = shmget(IPC_PRIVATE, sizeof(int), IPC_CREAT | IPC_EXCL | 0666);
 adults_inside = (int *) shmat(shm_a_id, NULL, 0);
 children_inside = (int *) shmat(shm_c_id, NULL, 0);
 sem_init(adults_sem,1,1);
 sem_init(children_sem,1,1);
 sem_init(entry,1,1);
}

этот код только имитирует генерацию процессов. Существует один общий семафор entry это позволяет только одному процессу в то время запрашивать ввод. Переменные общей памяти adults_inside и children_inside следите за внутренним состоянием.

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

2 ответов


вам нужно будет реализовать это с точки зрения какой-либо формы IPC. Вы упомянули использование Linux, но я предположу, что POSIX-with-unnamed-semaphores (т. е. не OS X), так как вы еще не используете ничего специфичного для Linux. Это может быть проще, если вы использовали нити. Но, возможно, у вас есть причина для использования нескольких процессов, поэтому я просто предположу это.

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

чтобы выяснить, как решить проблему, давайте рассмотрим, как такая вещь может быть обработана в реальной жизни. Можно представить, что в детском саду есть какой-то "привратник". Этот "привратник" представлен в коде суммой состояния: семафор и переменные общей памяти, представляющие количество взрослых и дети в любой момент времени. Когда дети не могут войти, привратник блокирует вход, и дети должны сформировать линию. Я предполагаю, что намерение состоит в том, что детям разрешено входить в первую очередь; это означает, что вам нужно будет иметь какой-то FIFO для представления очереди. Когда ребенок уходит, привратник должен иметь возможность уведомить первого ребенка в очереди, что они имеют право войти.

таким образом, этот код отсутствует два вещи:

  1. какой-то FIFO, представляющий порядок детей, ожидающих входа
  2. какой-то механизм уведомления, что ребенок может ждать уведомления, и что привратник может вызвать, чтобы "разбудить" ребенка.

теперь вопрос в том, какие данные мы храним в этой очереди и как мы делаем уведомление. Есть несколько вариантов, но я рассмотрю два наиболее очевидным.

Заставляя ребенка ждать, может быть так же просто, как если бы привратник поместил ребенка PID в хвост FIFO и отправил этот PID SIGSTOP используя kill(2). Это может произойти несколько раз. Как только ребенок уходит, привратник выносит из головы ФИФО и посылает пид SIGCONT.

как в настоящее время architected, "привратник" в вашей программе больше абстрактной концепции. Более четкая реализация может реализовать gatekeeper как процесс управления.

но поскольку такого процесса не существует, нам нужно представить себе что-то вроде того, как ребенок видит знак "пожалуйста, подождите" на двери и ждет. Процесс, представляющий ребенка, может заставить себя ждать, поместив себя в хвост ФИФО и используя raise(3) функция библиотеки и отправка себя SIGSTOP. Затем, когда любой ребенок уходит, он читает с передней части FIFO и отправляет этот pid SIGCONT используя kill(2).

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

альтернативным подходом было бы дать каждому ребенку свой собственный файловый дескриптор(ы). Это может быть либо pipe(2), или двунаправленный файловый дескриптор как PF_LOCAL socket(2). Оставляя файловые дескрипторы в режиме блокировки, когда ребенку не разрешено входить, он помещает (возможно, сторону записи, если канал) свой файловый дескриптор в хвост FIFO и блокирует read(2)ing один байт со стороны чтения (который был бы тем же fd, если бы не труба.)

когда ребенок выходит, он тянет вход с передней части FIFO и write(2)s один байт в файловый дескриптор. Это разбудит дочерний процесс, который заблокирован в read(2), и он будет продолжать свой веселый путь в детский сад.

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

в сигнал и случай файлового дескриптора, кольцевого буфера целых чисел достаточно - так что это все состояние, которое вам нужно сохранить в FIFO.

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


2 семафора точно моделируют актуальную проблему

хотя соблазнительно объединить статистику в синхронизацию, минимум, который вам нужно синхронизировать для этого ухода за ребенком, действительно только:

  • количество вакансий для детей > 0 для ребенка, чтобы войти, иначе они ждать.
  • выходя взрослые принимают их 3 вакансии атомарно по отношению друг к другу или ждать. (Один взрослый отказывается принимать больше ответственность на выходе не является явной в вашей модели, но предотвращает голод выхода, подобный голоду писателя в реализациях rwlock.)

но они должны быть нанесены на карту точно

когда семафоры нажмите 0 Они силу ждать, поэтому, чтобы смоделировать это с помощью 2 семафоров, которые вы начали настраивать, их использование должно соответствовать еще нескольким спецификациям:

 sem_init(adults_exiting_sem,1,1); /* Allow 1 adult to be decrementing */
 sem_init(children_spots_sem,1,0); /* Allow no child without an adult */

тогда код обработчика может полагаться на правильную модель семафоров чтобы заставить ждать:

void handler(int pid,int maxtime, char* type)
{
  int leave_t = (random() % (maxtime + 1));

  if(type == "child")
  {

    printf("%s %i%s\n",type,pid," attempting to enter...");
    sem_wait(children_spots_sem);

    printf("%s %i%s\n",type,pid," entered!");
    sleep(leave_t);

    sem_post(children_spots_sem);
  }
  else if(type == "adult")
  {
    /* probably an inline function */
    sem_post(children_spots_sem);
    sem_post(children_spots_sem);
    sem_post(children_spots_sem);

    printf("%s %i%s\n",type,pid," entered.");
    sleep(leave_t);
    printf("%s %i%s\n",type,pid," attempting to leave...");

    /* adult exit funnel */
    sem_wait(adults_exiting_sem);

    /* probably an inline function */
    sem_wait(children_spots_sem);
    sem_wait(children_spots_sem);
    sem_wait(children_spots_sem);

    sem_post(adults_exiting_sem);

  }
    printf("%s %i%s\n",type,pid," left!");
}

и всегда хочу!--6-->

естественно, вы можете расширить модель:

  1. использовать sem_timedwait чтобы моделировать родителей, отказывающихся от высадки своих детей.
  2. повторное введение статистики либо с дополнительной синхронизацией, либо просто войдите в систему для анализа сообщений.