父进程在子进程之前执行,如何通过信号量强制反对?

时间:2013-05-26 18:19:22

标签: c memory fork semaphore shared

我在Unix C中使用共享内存,信号量和分叉时遇到问题。我的信号量不是posix。 我创建了一个指向共享内存2 * sizeof(float)的指针。 我使用semctl将我的信号量的值初始化为2。 我在for循环中执行fork()(i <2)。 在子进程中(如果fork()== 0),每个子进程对信号量(-1)执行p操作,写入共享内存然后执行v操作(+1)然后退出。 Parent进程对信号量执行p操作(-2),读取整个共享内存段(带有for循环)并执行v操作(+2)。他在退出之前等待子进程以避免僵尸。 我输出的问题是我得到:

 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个孩子一个接一个地写入相同大小的共享内存段,只有当它已满时,父母才能读取所有内容并处理信息。最重要的是,所有这些都需要在一个循环中(想象24辆汽车每次完成一圈时都会产生随机时间,直到第一辆车完成50圈)

1 个答案:

答案 0 :(得分:4)

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

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

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


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

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

最终(我希望)编辑:在解决较小的问题之后,最后剩下的一个是在每个子进程中使用pthread_mutex_init(),这将使用V(+1)来表示“生成的数据和所有完成“然后SEM_UNDOexit记录在进程退出时应用的平衡调整值,因此信号量将计数,但随后立即向右计数,让父进程等待另一个永远不会发生的V.