【问题标题】:Parent is executing before child process, how to force opposite through semaphores?父进程在子进程之前执行,如何通过信号量强制相反?
【发布时间】:2013-05-21 16:01:12
【问题描述】:

我在 Unix C 中使用共享内存、信号量和分叉时遇到问题。我的信号量不是 posix。 我创建了一个指向共享内存 2*sizeof(float) 的指针。 我用 semctl 将信号量的值初始化为 2。 我在 for 循环(i

 Parent reading 0
 Parent reading 1
 Parent reading 0
 Parent reading 1
 Child writing 0
 Child writing 1

什么时候我应该得到的是:

 Child writing 0
 Child writing 1
 Parent reading 0
 Parent reading 1

我尝试将我的信号量初始化为 1 而不是 2,但这只会让程序停止,因为信号量永远不会有值 2,因此父进程永远不会读取。

如果我对信号量的理解是正确的,我将它初始化为 2 的事实意味着即使没有子进程写任何东西,父进程也可以直接读取。我该如何解决这个问题?

编辑:我在请求后添加了我的代码的简化版本,我删除了错误检查,并等待孩子减少长度。

/** OPEN MEMORY **/

int shmid1 = shmget(1990, (size),  IPC_CREAT | 0666 ); 

float * shmPt = (float*)shmat(shmid1, NULL, 0);    

/** CREATE INITIALIZE SEMAPHORE **/   

semun1.val = 2;

int semid = semget(1991, 1, 0666 | IPC_CREAT)

semctl(semid, 0, SETVAL, semun1 )


  /**  CREATE PROCESSES **/
  for ( ii = 0; ii < 2; ++ii) {
    if ((p = fork()) == 0) {

      int semid = semget(1991, 1, 0666);

      struct sembuf p_buf;

      p_buf.sem_num = 0;p_buf.sem_op = -1;p_buf.sem_flg = SEM_UNDO;
      /** LOCK **/ 
      semop(semid, &p_buf,1);
      /** WRITE **/
      shmPt[ii] = RandomFloat;

      v_buf.sem_num = 0;v_buf.sem_op = 1;v_buf.sem_flg = SEM_UNDO;
      /** UNLOCK **/
      semop(semid, &v_buf,1) 

      exit(0);
    }
    else {

      int semid = semget(1991, 1, 0666);

      struct sembuf p_buf;

      p_buf.sem_num = 0;p_buf.sem_op = -2;p_buf.sem_flg = SEM_UNDO;
      /** LOCK **/ 
      semop(semid, &p_buf,1);
      /** READ **/
      for(int j =0;j<2;j++) tabFloat[j] = shmPt[j];

      v_buf.sem_num = 0;v_buf.sem_op = 2;v_buf.sem_flg = SEM_UNDO;
      /** UNLOCK **/
      semop(semid, &v_buf,1) 

    }
}

编辑: 我的最终目标是让 24 个孩子一个一个地写到一个相同大小的共享内存段中,只有当它写满时,父母才能读取所有内容并处理信息。最重要的是,所有这些都需要在一个 while 循环中进行(想象一下 24 辆汽车在每次完成一圈时不断生成随机时间,直到第一辆汽车完成 50 圈)

【问题讨论】:

  • 与其描述代码,为什么不显示给我们呢?那时你会更容易看到和理解你在做什么。
  • 与其试图描述你的代码,为什么不直接发布你的代码呢? (如有必要,以简化形式。)
  • 我添加了简化代码

标签: c memory fork semaphore shared


【解决方案1】:

您误用了信号量。一般的想法是,信号量计算“现在允许有多少实体(线程,等等)使用这些数据”。通过从 2 开始计数,您是在说“两个线程现在可以使用它”。信号量不说哪些实体,也不说如何(读与写),只说有多少。例如,信号量可用于计算生产者/消费者队列中可检索项目的数量:生产者递增,消费者递减。 (当然,信号量有各种扩展风格;既然你说这些不是 POSIX,但不是它们,很难概括更多。)

按照描述进行这项工作的一种方法(当然,实际代码往往与描述不同)是将信号量计数从 0 开始,派生一个孩子,让孩子在不查看信号量计数的情况下编写,再派生另一个孩子,让那个孩子也写而不看信号量,然后让父母在信号量(P)上等待。也就是说,信号量说“没有人会通过”,但孩子们实际上并没有看它。然后,两个孩子各自做 V 操作(每个 +1)。一旦信号量变为 1,父母就开始:然后他可以找到至少一个(但也许只有一个)子结果。如果父母需要同时获得这两个结果,他可以立即执行另一个 P。

(更一般地说,您可能需要读/写锁或互斥锁和条件变量。如果您有 POSIX 线程,请参阅pthread_cond_init()pthread_cond_wait()pthread_cond_signal()pthread_mutex_init() 等)


啊哈,从评论和问题编辑中,我看到您正在使用可恶的 System V 共享内存和信号量接口。

  • 你真的坚持吗?在我看来,POSIX 线程的东西更好(而且通常更轻)。
  • 您打算如何组织您的共享内存?如果每辆车都有自己的单圈时间区域,仅与显示线程/proc 共享,您可能会减少锁争用:有一个生产者(汽车)和一个消费者(显示线程/proc),但有 24 个这样的锁(每辆车一个)。如果所有汽车与显示线程/proc 共享一个共享内存区域,则您只需要一个锁,但它更活跃。哪个“更好”取决于您在做什么。
  • 而且,如果您想等待“某辆车跑完 50 圈”,请考虑让每辆车都有自己的私人(或可能与显示器共享)计数器,以及一个用于“有打了 50 圈”。每辆汽车只是简单地计数,当达到 50 时,计数信号量也会增加(一次)。

最后(我希望)编辑:在修复了较小的问题后,最后剩下的一个是在每个子进程中使用SEM_UNDO,它会做一个 V(+1)来表示“数据已生成并全部完成”然后exitSEM_UNDO 记录了在进程退出时应用的平衡调整值,因此信号量会向上计数,但随后会立即向下计数,让父级等待另一个永远不会发生的 V。

【讨论】:

  • 我明白你在说什么,但我的最终目标是让 24 个孩子一个接一个地写入相同大小的共享内存段,并且只有当它已满时,父母才能读取所有内容并处理信息。最重要的是,所有这些都需要在一个 while 循环中进行(想象一下 24 辆汽车在每次完成一圈时不断生成随机时间,直到第一辆汽车完成 50 圈)。
  • 是的,恐怕我会坚持下去。当我在 for 循环中分叉 24 次时,我将“i”作为参数传递给每个子进程,并且每个子进程都在不同的位置写入共享内存。所以进程 0 正在写入 shmPt[0] 等等。我只想让所有 24 个孩子一个接一个地写,然后消费者在所有孩子都写完之后一口气读完所有内容,并根据需要循环所有内容,我需要一种方法来使用信号量来做到这一点。
  • 顺便说一句,每辆车都有自己的私人圈数计数器,每圈后我对数组进行排序以找到最快的汽车并检查其圈数计数器以与 main 中的 while 循环中的 MAXLAPS 常数进行比较。跨度>
  • 在这种情况下,父母只需要等到至少一个孩子(私下)计数到 50,然后在任何分叉之前递增(单个)“孩子完成”计数信号量为 0 ();或者,它需要等到所有孩子都(私下)计数到 50 并增加他们的“孩子完成”信号量。前者,parent对计数信号量做一次P(),children每人做一次V();对于后者,父母做了 24 个 P()s。但是,如果你是 fork(),你可以只使用 wait() 而不是一开始就使用计数信号量。
  • 好的,这有点远了,但是在我面前有手册页,我看到:“如果指定了 SEM_UNDO,则信号量对退出值的调整会减少 sem_op 的值。”你的代码确实设置了SEM_UNDO,所以我怀疑这就是在这里咬你的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-22
  • 2023-04-01
  • 2020-03-23
  • 2022-12-10
  • 1970-01-01
  • 2019-06-02
相关资源
最近更新 更多