【问题标题】:C - Shared memory and semaphoresC - 共享内存和信号量
【发布时间】:2016-06-02 11:00:24
【问题描述】:

我想创建一个具有共享内存和信号量的 C 程序。应该有两个子进程。两个孩子都有不同的 int 号。然后有一个应该写入共享内存的目标编号。现在两个孩子都应该从目标数字中减去他们的数字,直到目标数字小于或等于 0。我不希望出现竞争条件。这就是为什么我尝试使用信号量。但这对我不起作用。这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <errno.h>
#include <sys/sem.h>

#define SEG_SIZE sizeof(int)
#define NUM_OF_CHILDS 2

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

    int i, shm_id, sem_id, *shar_mem;
    int pid[NUM_OF_CHILDS];
    long waittime = 100;
    unsigned short marker[1];

    /* Define the numbers and the goal number */

    int numbers[2] = {28, 23};
    int goal = (numbers[0] + numbers[1]) * 4;   

    /* Create semaphor */

    if((sem_id = semget(IPC_PRIVATE, 1, IPC_CREAT|0644)) == -1){

        perror("semget()");
        exit(EXIT_FAILURE);

    }

    marker[0] = 1;

    /* All sem's to 1 */

    semctl(sem_id, 1, SETALL, marker);

    /* Create shared memory */

    if((shm_id = shmget(IPC_PRIVATE, SEG_SIZE, IPC_CREAT|0600)) == -1){

        perror("shmget()");
            exit(EXIT_FAILURE); 

    }
    if((shar_mem = (int *)shmat(shm_id, 0, 0)) == (int *) -1){

        perror("shmat()");
        exit(EXIT_FAILURE); 

    }
    *shar_mem = goal;

    /* Create child processes */

    for(i = 0; i < NUM_OF_CHILDS; i++){

        pid[i] = fork();
        if(pid[i] < 0){

            printf("Error!\n");
            exit(1);

        }
        if(pid[i] == 0){
            int count = 0;  
            /* Child processes */

            /* Structs for semaphor */

            struct sembuf enter, leave;

            enter.sem_num = leave.sem_num = 0;      
            enter.sem_flg = leave.sem_flg = SEM_UNDO;
            enter.sem_op = -1;              /* DOWN-Operation */
            leave.sem_op = 1;               /* UP-Operation */

            /* Join critical area */

            semop(sem_id, &enter, 1);

            while(*shar_mem > 0){

                usleep(waittime);
                *shar_mem -= numbers[i];

                count++;
            }

            printf("%i\n", count);

            /* Leave critical area */

            semop(sem_id, &leave, 1);

            exit(0);

        }

    }

    /* Wait for childs. */

    for(i = 0; i < NUM_OF_CHILDS; i++){

        waitpid(pid[i], NULL, 0);

    }

    /* Is goal equal 0 or lower? */

    int returnv;

    if(*shar_mem == 0){

        /* No race conditions */

        returnv = 0;

    }
    else {

        /* Race conditions */

        returnv = 1;

    }

    /* Close shared memory and semaphores */

    shmdt(shar_mem);
    shmctl(shm_id, IPC_RMID, 0);
    semctl(sem_id, 0, IPC_RMID);

    return returnv;

}

当共享内存值最后为 0 时,应该没有竞态条件。如果它低于 0,则存在竞争条件。而且我总是低于0。最重要的是,我计算每个孩子减去他的数字的次数。结果:第一个孩子 8 次,第二个孩子 0 次。谁能帮帮我?

【问题讨论】:

  • 您的 while(*shar_mem) 循环在 semop(enter) .. semop(leave) 部分内。您可能只希望一个孩子同时处理这个数字,而且只需要一次,所以应该颠倒这个顺序。那时,互斥锁会比信号量更合适。
  • 这应该如何工作?第二个子进程永远没有机会减去它的数字,因为第一个子进程在目标小于或等于 0 之前没有离开临界区域。在您的情况下,目标将导致 -24,即 (28+23)* 4 - 8*28。即使调整你的 semop 调用,你也不能保证两个孩子交替减去他们的数字,所以结果代码可能是零,但也可能不是
  • 好主意,但我试图将 semop 部分放入循环中。现在第一个孩子有 8 个计数,第二个孩子有 1 个计数。所以变化不大。这是一个学习练习,我必须使用信号量。
  • 也许题外话,但您可以尝试sem_init 与非零pshared 来获得进程之间的共享信号量。

标签: c fork semaphore shared-memory race-condition


【解决方案1】:

将您的 while 循环调整为类似这样,以便在减去一次后离开关键部分:

for ( ; ; ) {
  usleep(waittime);
  semop(sem_id, &enter, 1);
  if (*shar_mem <= 0) {
    semop(sem_id, &leave, 1);
    break;
  }
  *shar_mem -= numbers[i];
  semop(sem_id, &leave, 1);
  count++;
}

但正如我的评论中所述,不能保证两个孩子交替减去他们的数字,即结果可能小于零

【讨论】:

    【解决方案2】:

    您在 cmets 中表示:

    我试图将 semop 部分放入循环中。现在第一个孩子有 8 个计数,第二个孩子有 1 个计数。 [...] 我必须使用信号量。

    显然,仅仅将临界区边界移动到循环内部是不够的。这是因为不能保证当一个进程通过增加信号量离开临界区时,另一个进程会立即进入临界区。相反,第一个进程可能会在第二个进程进入临界区之前循环并重新进入临界区。事实上,这个结果很可能是因为循环很紧,而且我们知道退出临界区的进程当前被调度在某个 CPU 上。

    可以通过在临界区之外的循环中引入延迟来解决该问题,但这有点笨拙。一方面,它不必要地减慢了程序的速度。另一方面,尽管足够长的延迟会使问题极不可能出现,但没有延迟量可以保证问题不会出现。您总是受系统调度程序的支配。但是,由于您已经有延迟,您可以通过将其也放在临界区之外来使您想要的行为极有可能表现出来。

    有几种方法可以提供各种理想的语义而无需人为延迟。所有这些都涉及至少一个信号量。以下是两个更有可能:

    • 为每个进程创建一个单独的信号量。选择你初始化为 1 的一个,并将其余的初始化为 0。在循环的顶部,每个进程递减它自己的信号量,而在底部它递增的是 next 进程的信号量。然后这些进程以循环方式运行。

    • 除了用于保护临界区的信号量之外,还使用两个额外的信号量在循环顶部创建一个“循环屏障”。循环屏障是一种可重用的结构,它会导致一些固定数量的进程/线程在它们中的任何一个可以继续之前都达到给定点。这不会使流程严格交替,但它可以防止任何流程在其他(S)之前超过一个循环迭代。它的优点是无论协调多少进程,它都只需要三个信号量。

    前者更易于概念化和编写,尽管它对流程顺序提供了更强的保证。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-27
      • 1970-01-01
      • 2010-10-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多