草庐IT

c - Linux 被动等待条件更新

coder 2023-06-18 原文

我正在尝试创建一个模拟托儿中心的代码。在这个中心,一名成年人最多可以照顾三个 child 。这个条件必须一直满足。成人和 child 是随机生成的过程, child 和成人的数量在程序参数中设置。 child 只有在里面有足够的成年人才能进入,成年人只有在有足够的其他成年人照顾 child 的情况下才能离开。如果不是,则应实现被动等待,直到条件允许 child /成人离开/进入。

#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%s\n",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%s\n",type,pid," entered!");

       usleep(child_leave_t*1000);

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

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

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

  else if(type == "adult")
  {

    (*adults_inside)++;
    printf("%s %i%s\n",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_insidechildren_inside跟踪内部状态。

我的问题基本上位于处理程序函数中。在触发不允许 child 进入的条件后,我无法弄清楚如何实现被动等待。我正在考虑使用 pause()系统调用并将等待的进程存储在队列中,但似乎效率很低。我应该选择什么方法?

最佳答案

您将需要根据某种形式的 IPC 来实现这一点。您提到使用 Linux,但我将假设 POSIX-with-unnamed-semaphores(即不是 OS X),因为您还没有使用任何特定于 Linux 的东西。其他人提到,如果您使用线程,这可能会更简单。但也许您有使用多个进程的原因,所以我只是假设。

正如指定的那样,代码似乎不允许成年人退出,这使事情变得简单一些。您已经知道在任何时间点允许有多少 child ,因为这与任何给定时间点在场的成年人数量成正比。

为了弄清楚如何解决这个问题,让我们考虑一下在现实生活中如何处理这样的事情。我们可以想象,托儿所里有某种“看门人”。这个“看门人”在代码中由状态的总和表示:信号量和共享内存变量,代表在任何时间点出现的成人和 child 的数量。当没有 child 被允许进入时,看门人会阻止进入, children 必须排成一列。我认为其目的是允许 child 以先到先得的方式进入;这意味着您需要使用某种 FIFO 来表示队列。当 child 离开时,看门人必须能够通知排队的第一个 child 他们有资格进入。

所以这段代码缺少两件事:

  • 某种FIFO代表等待进入的 child 的顺序
  • 某种通知机制, child 可以等待通知,并且网守可以触发“唤醒” child 。

  • 现在,问题是我们在这个队列中存储了哪些数据以及我们如何进行通知。有多种选择,但我将讨论最明显的两个。

    让子进程等待可能就像让网守将子进程 PID 放在 FIFO 的尾部并发送该 PID SIGSTOP 一样简单。使用 kill(2) .这可能会发生多次。一旦 child 离开,网守从 FIFO 的头部出列并发送 pid SIGCONT .

    按照目前的架构,程序中的“看门人”更像是一个抽象概念。更清晰的实现可能会将看门人实现为管理过程。

    但是由于不存在这样的过程,我们需要想象这样的事情,比如 child 在门口看到“请稍等”的标志并等待。代表子进程的进程可以通过将自己放在 FIFO 的尾部并使用 raise(3) 来让自己等待。库函数,并发送自身 SIGSTOP .然后,当任何 child 离开时,它从 FIFO 的前端读取并发送该 pid SIGCONT使用 kill(2) .

    这个实现相对简单,唯一需要的额外资源是在共享内存中以某种方式表示队列。

    另一种方法是给每个 child 自己的文件描述符。这可能是 pipe(2) ,或双向文件描述符,如 PF_LOCAL socket(2) .让文件描述符处于阻塞模式,当一个子进程不被允许进入时,它把它的文件描述符放在 FIFO 的尾部(可能是写端,如果是管道),并阻塞 read(2)从读取端读取一个字节(如果不是管道,它将是相同的 fd)。

    当 child 退出时,它会从 FIFO 和 write(2) 的前面拉入条目。 s 一个字节到那里的文件描述符。这将唤醒在 read(2) 中阻塞的子进程,它将继续愉快地进入托儿所。

    如前所述,还建议了条件变量。我通常会避开它们;它们很容易被误用,而且您已经在序列化输入过程。

    在信号和文件描述符的情况下,整数环形缓冲区就足够了——这就是您需要存储在 FIFO 中的所有状态。

    FIFO 需要仔细考虑。由于多个进程将读取和操作它,因此它也必须在共享内存中。无论 FIFO 是作为环形缓冲区还是以其他方式实现,您都可能希望对队列的长度进行一些限制。如果排队的 child 太多,也许到达的 child 只是“回家”。此外,您需要确保在进入/退出时优雅地处理空 FIFO 的情况,并确保从 1 服务员到 0 的转换按预期工作。由于您使用信号量序列化进入/退出,因此这应该很简单。

    关于c - Linux 被动等待条件更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43478894/

    有关c - Linux 被动等待条件更新的更多相关文章

    1. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

      给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

    2. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

      我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

    3. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

      我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

    4. ruby - 在 Ruby 中有条件地定义函数 - 2

      我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

    5. ruby - 定义方法参数的条件 - 2

      我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

    6. objective-c - 在设置 Cocoa Pods 和安装 Ruby 更新时出错 - 2

      我正在尝试为我的iOS应用程序设置cocoapods但是当我执行命令时:sudogemupdate--system我收到错误消息:当前已安装最新版本。中止。当我进入cocoapods的下一步时:sudogeminstallcocoapods我在MacOS10.8.5上遇到错误:ERROR:Errorinstallingcocoapods:cocoapods-trunkrequiresRubyversion>=2.0.0.我在MacOS10.9.4上尝试了同样的操作,但出现错误:ERROR:Couldnotfindavalidgem'cocoapods'(>=0),hereiswhy:U

    7. ruby-on-rails - 在 Ruby on Rails 中发送响应之前如何等待多个异步操作完成? - 2

      在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.

    8. ruby-on-rails - 使用包含多个关联和单独的条件 - 2

      我的Gallery模型中有以下查询:media_items.includes(:photo,:video).rank(:position_in_gallery)我的图库模型有_许多媒体项,每个都有一个照片或视频关联。到目前为止,一切正常。它返回所有media_items包括它们的photo或video关联,由media_item的position_in_gallery属性排序。但是我现在需要将此查询返回的照片限制为仅具有is_processing属性的照片,即nil。是否可以进行相同的查询,但条件是返回的照片等同于:.where(photo:'photo.is_processingIS

    9. ruby-on-rails - 在 haml View 中重构条件 - 2

      除了可访问性标准不鼓励使用这一事实指向当前页面的链接,我应该怎么做重构以下View代码?#navigation%ul.tabbed-ifcurrent_page?(new_profile_path)%li{:class=>"current_page_item"}=link_tot("new_profile"),new_profile_path-else%li=link_tot("new_profile"),new_profile_path-ifcurrent_page?(profiles_path)%li{:class=>"current_page_item"}=link_tot("p

    10. ruby-on-rails - Rails Associations 的更新方法是什么? - 2

      这太简单了,太荒谬了,我在任何地方都找不到关于它的任何信息,包括API文档和Rails源代码:我有一个:belongs_to关联,我开始理解当您没有关联时您在Controller中调用的正常模型方法与您有关联时调用的方法略有不同。例如,我的关联在创建Controller操作时运行良好:@user=current_user@building=Building.new(params[:building])respond_todo|format|if@user.buildings.create(params[:building])#etcetera但我找不到关于更新如何工作的文档:@user

    随机推荐