简体   繁体   English

C-共享内存和信号量

[英]C - Shared memory and semaphores

I want to create a C program with shared memory and semaphores. 我想创建一个具有共享内存和信号量的C程序。 There should work two child processes. 应该有两个子进程。 Both childs got a different int number. 两个孩子都有不同的int数。 Then there is a goal number which should be written in the shared memory. 然后有一个目标号应写入共享内存中。 Now both childs should subtract their numbers from the goal number until the goal number is lower or equal 0. I dont want that there appear race conditions. 现在,两个孩子都应该从目标编号中减去其编号,直到目标编号小于或等于0。我不希望出现比赛条件。 Thats why I try to use semaphores. 这就是为什么我尝试使用信号量的原因。 But it dont work for me. 但这对我不起作用。 Here is my code: 这是我的代码:

#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;

}

When the shared memory value is 0 at the end, there should be no race conditions. 最后,如果共享内存值为0,则不应存在争用条件。 If it is lower than 0, there were race conditions. 如果它小于0,则说明存在竞争条件。 And I always get lower than 0. On top of that I count how much times each child subtract his number. 而且我总是低于0。最重要的是,我计算每个孩子减去他的数字的次数。 The result: First Child 8 times and the second one 0 times. 结果:第一个孩子8次,第二个孩子0次。 Can anyone help me with it? 有人可以帮我吗?

Adapt your while loop to something like this in order to leave the critical section after subtracting one time: 使您的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++;
}

But as stated in my comment it's not guaranteed that both children subtract their numbers alternating, ie a result less than zero is possible 但是,正如我的评论中所述,不能保证两个孩子都交替减去他们的数字,即结果可能小于零

In comments you remarked: 在您评论的评论中:

I tried to put the semop part into the loop. 我试图将semop部分放入循环中。 Now there 8 counts for the first child and 1 for the second. 现在第一个孩子有8个计数,第二个孩子有1个计数。 [...] I have to use semaphore. [...]我必须使用信号量。

Clearly, just moving the critical section boundaries into the interior of the loop is not sufficient. 显然,仅将关键部分边界移入循环内部是不够的。 This is because there is no guarantee that when one process leaves the critical section by incrementing the semaphore, the other will immediately enter the critical section. 这是因为无法保证当一个进程通过增加信号量而离开关键部分时,另一进程将立即进入关键部分。 It is possible that instead, the first process will loop around and re-enter the critical section before the second enters it. 相反,第一个过程可能会循环,然后在第二个部分进入关键部分之前重新输入它。 In fact, that result is fairly likely because the loop is tight, and we know that a process exiting the critical section is currently scheduled on some CPU. 实际上,该结果很可能是因为循环很紧,而且我们知道退出关键部分的进程当前已在某些CPU上调度。

It is possible to approach that problem by introducing a delay into the loop, outside the critical section, but this is a bit kludgy. 可以通过在关键部分之外的环路中引入延迟来解决该问题,但这有点麻烦。 For one thing, it needlessly slows the program. 一方面,它不必要地减慢了程序速度。 For another, although a sufficiently long delay can make the problem highly unlikely to manifest, no amount of delay can guarantee that the problem will not manifest. 另一方面,尽管足够长的延迟可以使问题极不可能出现,但是没有多少延迟可以保证问题不会显现。 You're always at the mercy of the system's scheduler. 您始终受制于系统调度程序。 Inasmuch as you already have a delay, however, you could make your desired behavior highly likely to manifest by putting it, too, outside the critical section. 但是,由于您已经有了延迟,因此您也可以通过将其置于关键部分之外,使所需的行为很可能得到体现。

There are a several ways to deal with this that provide various desirable semantics without artificial delays. 有几种处理此问题的方法,可以提供各种所需的语义而不会造成人为的延迟。 All of them involve at least one more semaphore. 它们全部涉及至少一个以上的信号量。 Here are two of the more likely: 以下是两种可能性:

  • Create a separate semaphore for each process. 为每个进程创建一个单独的信号灯。 Choose one that you initialize to 1, and initialize the rest to 0. At the top the loop, each process decrements its own semaphore, and at the bottom it increment's the next process's semaphore. 选择一个初始化为1的变量,将其余初始化为0的变量。在循环的顶部,每个进程都会减少自己的信号量,在底部,它会增加下一个进程的信号量。 The processes then run in round-robin fashion. 然后,这些过程以循环方式运行。

  • Use two additional semaphores to create a "cyclic barrier" at the top of the loop, in addition to the semaphore that you're using to protect the critical section. 除了用于保护关键部分的信号灯之外,还可以使用两个其他信号灯在循环的顶部创建“循环屏障”。 A cyclic barrier is a re-usable construct that causes some fixed number of processes / threads to all reach a given point before any of them can proceed. 循环屏障是一种可重用的构造,它使一些固定数量的进程/线程在它们中的任何一个可以继续进行之前都到达给定的点。 This does not make the process strictly alternate, but it prevents any process from ever being more than one loop iteration ahead of the other(s). 这不会使进程严格交替,但可以防止任何进程比其他进程提前一个以上的循环迭代。 It has the advantage that it requires only three semaphores regardless of how many processes are being coordinated. 它的优点是,无论要协调多少个进程,它都只需要三个信号灯。

The former is easier to conceptualize and write, even though it places stronger guarantees on process ordering. 前者更容易概念化和编写,即使它为流程订购提供了更有力的保证。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM