进程似乎不会减少信号量

Processes does not seem to decrement semaphore

我的 c 程序有问题。
我已经在这里写了一个关于同一个程序的主题(我有 fork 的问题):

我正在模拟 F1 练习。现在分叉,我得到了一些不相关的数据(1/3 的汽车时间错误(0min0s0ms)。
我认为这是一个并发问题,所以我尝试 实现一个信号量 。 这样做的目的是一次只允许 1 个进程写入共享内存。

但是现在,我所有的输出数据都是错误的

例如:

  • 大多数飞行员号码不正确(很多 n°0)。
  • 他们都有不切实际的最佳时间 (0min 0sec 0ms)。

我注意到的一件事是,如果我删除增加信号量 1 (sem + 1)sem 操作在写入共享内存之前没有进程被阻塞但是他们应该因为信号量永远不会递增(它只是还在递减).

这是 "make the race":

的程序
/* /!\ COMPILER AVEC -lm && -lpthread /!\ */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sem.h>
#include <time.h>
#include <string.h>
#include <math.h>
#include <wait.h>

#include "CourseF1.h"
#include "ResultCourse.h"

#define MAX_PILOTES 22
#define MAX_TOURS 44

int semid;

float ranf() { // PRNG for floats [0, 1].
    float r = rand() / (float) RAND_MAX;
    return r;
}

/**
*
* method based on Box-Muller transformation: https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform
*
**/
float randGaussien(float m, float s) { /* median m, standard deviation s */
    float x1, x2, w, y1, result;
    float y2;
    int use_last = 0;

    if (use_last) /* use value of last call */
    {
        y1 = y2;
        use_last = 0;
    }
    else
    {
        do {
            x1 = 2.0 * ranf() - 1.0;
            x2 = 2.0 * ranf() - 1.0;
            w = x1 * x1 + x2 * x2;
        } while ( w >= 1.0 );

        w = sqrt( (-2.0 * log( w ) ) / w );
        y1 = x1 * w;
        y2 = x2 * w;
        use_last = 1;
    }

    result = ( m + y1 * s );
    if (result < 0) {
        return 0.0;
    }
    return result;
}


int genTime(const int min, const int max) {
    return ((rand() % (max-min + 1)) + min); // Generate a random number between min and max
}

int genRaceEvents(const int max) { // Decide about race events
    return rand() % max; // Generate a number between 0 and max - 1
}

int compareBest(const void *p1, const void *p2) { // Compare the best times
    const struct Pilote *elem1 = p1;
    const struct Pilote *elem2 = p2;

    if (elem1->best < elem2->best) return -1;
    if (elem1->best > elem2->best) return 1;
    return 0;
}

int compareTot(const void *p1, const void *p2) { // Compare the total times
    const struct Pilote *elem1 = p1;
    const struct Pilote *elem2 = p2;

    if (elem1->totalTime < elem2->totalTime) return -1;
    if (elem1->totalTime > elem2->totalTime) return 1;
    return 0;
}

void fillTab(struct Pilote tabToFill[], struct Pilote tabFiller[], const int start, const int stop) {
    for (int i = start; i < stop; i++) {
        tabToFill[i] = tabFiller[i];
    }
}

int run(Pilote *p, char* name) {
    struct sembuf sem_op; // sembuf struct for semaphore operations

    sem_op.sem_num = 0;
    sem_op.sem_op = -1; // sem - 1
    sem_op.sem_flg = 0;

    if (semop(semid, &sem_op, 1) == -1) { // sem operation
        perror("Error when decrementing semaphore");
    }

    /* Instantiation of Pilote struct values */
    p->s1 = 3 * 60 * 3600 + 1;
    p->bestS1 = 3 * 60 * 3600 + 1;
    p->s2 = 3 * 60 * 3600 + 1;
    p->bestS2 = 3 * 60 * 3600 + 1;
    p->s3 =  3 * 60 * 3600 + 1;
    p->bestS3 = 3 * 60 * 3600 + 1;
    p->best =  3 * 60 * 3600 + 1;
    p->totalTime = 0;
    p->isPit = 0;
    p->hasGivenUp = 0;
    p->hasGivenUpDuringRace = 0;
    p->numberOfPits = 0;

    for (int i = 0; i < MAX_TOURS; i++) { // For every lap

        p->isPit = 0; // At first he doesn't pit

        if (!(p->hasGivenUp)) { // If the pilote didn't give up

            int givingUpEvent = genRaceEvents(500); // Generate a number between 0 and 499

            if (givingUpEvent == 14 && strcmp(name, "Race") == 0) { // If the pilote has given up (during race)
                p->best = 3 * 60 * 3600;
                p->hasGivenUpDuringRace = 1;
                return 0; // Stop le pilote
            }

            else if (givingUpEvent == 14) { // If the pilote has given up (but not during race)
                p->best = 3 * 60 * 3600 + 3;
                p->hasGivenUp = 1;
                return 0; // Stop the pilote
            }
        }

        if (p->numberOfPits < 2) { // Max 2 stops
            p->isPit = genRaceEvents(250); // Generate a number between 0 and 249

            if (p->isPit) {
                p->numberOfPits++;
                if ((strcmp(name, "Practices") == 0)|| (strcmp(name, "Qualifs") == 0)) continue; // Next iteration
            }

        }

        // We do a lap
        int S1 = 0.275 * (103000 + randGaussien(5000, 2000)); // Portion of circuit * Gauss curve (min time + fun(median, standard deviation))
        int S2 = 0.459 * (103000 + randGaussien(5000, 2000));
        int S3 = 0.266 * (103000 + randGaussien(5000, 2000));

        if ((strcmp(name, "Race") == 0) && (p->isPit)) { // If we are during race and the pilote pit
            S1 += genTime(20 * 3600, 25 * 3600); // We add between 20 and 25sec at Sector 1
        }

        p->s1 = S1; // Notify time of S1
        p->s2 = S2; // Notify time of S2
        p->s3 = S3; // etc...

        int lap = S1 + S2 + S3;

        if (p->bestS1 > S1) p->bestS1 = S1; // If it's its best S1, we modify the best S1
        if (p->bestS2 > S2) p->bestS2 = S2; // etc
        if (p->bestS3 > S3) p->bestS3 = S3; // etc

        if (p->best > lap) p->best = lap; // If it's its best lap time, we modify the best lap time, 

        if ((strcmp(name, "Race") == 0)) {
            p->totalTime += lap; // add the lap time to the total race time
        }

    } // End of for loop

    sem_op.sem_num = 0;
    sem_op.sem_op = 1; // sem + 1
    sem_op.sem_flg = 0;
    if (semop(semid, &sem_op, 1) == -1) { // sem operation
        perror("Error when incrementing semaphore");
    }   
}

int main(int argc, char const *argv[]) {
    //srand (time(NULL)); // Useful for random number generation

    printf("========================================\n");
    for (int i = 0; i < 22; i++) {
        printf("Random: %d\n", genRaceEvents(100));
    }
    printf("=========================================\n");

    // Variables for the race
    int pilotes_numbers[MAX_PILOTES]  = {44, 6, 5, 7, 3, 33, 19, 77, 11, 27, 26, 55, 14, 22, 9, 12, 20, 30, 8, 21, 31, 94}; // Tab containing pilotes numbers
    struct Pilote Q2[16]; // Tab of pilotes for Q2
    struct Pilote Q3[10]; // Tab of pilotes for Q3
    struct Pilote mainRun[MAX_PILOTES]; // Tab of pilotes for other race, practices
    struct Pilote *pilotesTab; // pointer to SM
    pid_t tabPID[MAX_PILOTES]; // Tab of PID
    int shmid = 0; // SM id
    key_t key; // Key for SM and Semaphores

     /**
     * Shared memory
     */

    // Key generation for Shared Memory
    key = ftok(argv[0], 123); // argv[O] => name of the program launched, ID (char)

    // SM initialization
    shmid = shmget(key, MAX_PILOTES * sizeof(Pilote), IPC_CREAT | 0644); 

    if (shmid == -1) {
        perror("Erreur when allocating SM.");
        return 0;
    }

    // Attach SM segment
    pilotesTab = shmat(shmid, NULL, 0);

    /**
     * Semaphores
     */

    // Semaphores initialization (1 semaphore)
    semid = semget(key, 1, IPC_CREAT | 0640); // key, number of semaphores, perm 

    if(semid == -1) { // Erreur
        perror("Error when creating semaphore");
        return 0;
    }

    // Set semaphore value to 1
    int rv = semctl(semid, 0, SETVAL, 1); // semid, sem number, operation type, union semun 

    if (rv == -1) { // sif return value == -1
        perror("Error when affecting value to the semaphore");
        return 0;
    }



    /**
     * Fork
     */

    int j;
    for (j = 0; j < MAX_PILOTES; j++) { /* Create 22 processes */

        tabPID[j] = fork();

        if (tabPID[j] == -1) { // Error
            perror("Error when forking\n");
            return 0;
        }

       if (tabPID[j] == 0) { // Child
            // 
            srand(time(NULL) ^ (getpid() << 16));
            pilotesTab[j].pilote_id = pilotes_numbers[j]; // Pilote number initialization
            run(&pilotesTab[j], "Practices");

            exit(0);
        } 
    } /* End of 22 processes */

    printf("==================================================== \n");
    fillTab(mainRun, pilotesTab, 0, MAX_PILOTES); // Fill the tab before sorting + show to the console
    showResults(mainRun, MAX_PILOTES, "Practices");
    printf("====================================================\n");

    /**
     * END TEST
     */
    semctl(semid, 0, IPC_RMID); // Semaphore remove
    shmdt(pilotesTab); // SM detach
    shmctl(shmid, IPC_RMID, 0); // SM remove
    return 0;
}

下面是在控制台中显示输出的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sem.h>
#include <time.h>
#include <string.h>
#include <math.h>
#include <semaphore.h>

#include "CourseF1.h"
#include "ResultCourse.h"

#define MAX_PILOTES 22

void showResults(struct Pilote tab[], int nbElems, char* name) {

    if (strcmp(name, "Race") != 0) { // NOT in Race
        qsort(tab, nbElems, sizeof(Pilote), compareBest);

        for (int k = 0; k < nbElems; k++) {

            // If the pilote gave up during session
            // But did a time
            if (tab[k].hasGivenUp) {
                printf(
                    "%d) voiture n°%d : Best S1 => %ds%dms | Best S2 => %ds%dms | Best S3 => %ds%dms | Best Lap => %dm%ds%dms || DNF\n", 
                    k+1,
                    tab[k].pilote_id, 
                    (tab[k].bestS1/1000)%60, tab[k].bestS1-(tab[k].bestS1/1000)*1000,
                    (tab[k].bestS2/1000)%60, tab[k].bestS2-(tab[k].bestS2/1000)*1000,
                    (tab[k].bestS3/1000)%60, tab[k].bestS3-(tab[k].bestS3/1000)*1000,
                    tab[k].best/60000, (tab[k].best/1000)%60, tab[k].best-(tab[k].best/1000)*1000
                );
                continue; 
            }

            // If the pilote gave up during first lap
            // HACK TIME to place it at the end of the list
            if (tab[k].hasGivenUp && tab[k].best == (3 * 60 * 3600) + 1) {
                printf("%d) voiture n°%d : // Abandon durant le premier tour de la session => Pas de temps // \n",
                    k+1,
                    tab[k].pilote_id
                );
                continue;
            }

            // If everything is OK
            printf(
                "%d) voiture n°%d : Best S1 => %ds%dms | Best S2 => %ds%dms | Best S3 => %ds%dms | Best Lap => %dm%ds%dms \n", 
                k+1,
                tab[k].pilote_id, 
                (tab[k].bestS1/1000)%60, tab[k].bestS1-(tab[k].bestS1/1000)*1000,
                (tab[k].bestS2/1000)%60, tab[k].bestS2-(tab[k].bestS2/1000)*1000,
                (tab[k].bestS3/1000)%60, tab[k].bestS3-(tab[k].bestS3/1000)*1000,
                tab[k].best/60000, (tab[k].best/1000)%60, tab[k].best-(tab[k].best/1000)*1000
            ); 
        }

    } else { // IN race
        for (int k = 0; k < nbElems; k++) {
            qsort(tab, nbElems, sizeof(Pilote), compareTot);

            if (tab[k].hasGivenUpDuringRace) {
                printf("%d) voiture n°%d: DNF (n'a pas pu finir l'entiereté de la course pour cause d'abandon)\n",
                    k+1,
                    tab[k].pilote_id
                );
                continue;
            } 

            printf(
                "%d) voiture n°%d : Best S1 => %ds%dms | Best S2 => %ds%dms | Best S3 => %ds%dms | Best Lap => %dm%ds%dms || Total => %dm%ds%dms \n", 
                k+1,
                tab[k].pilote_id, 
                (tab[k].bestS1/1000)%60, tab[k].bestS1-(tab[k].bestS1/1000)*1000,
                (tab[k].bestS2/1000)%60, tab[k].bestS2-(tab[k].bestS2/1000)*1000,
                (tab[k].bestS3/1000)%60, tab[k].bestS3-(tab[k].bestS3/1000)*1000,
                tab[k].best/60000, (tab[k].best/1000)%60, tab[k].best-(tab[k].best/1000)*1000,
                tab[k].totalTime/60000, (tab[k].totalTime/1000)%60, tab[k].totalTime-(tab[k].totalTime/1000)*1000
            ); 
        }
    }

}

这是控制台中的错误输出:

1: voiture n°44: (0m0s0ms)
2: voiture n°6: (0m0s0ms)
3: voiture n°5: (0m0s0ms)
4: voiture n°0: (0m0s0ms)
5: voiture n°3: (0m0s0ms)
6: voiture n°0: (0m0s0ms)
7: voiture n°0: (0m0s0ms)
8: voiture n°0: (0m0s0ms)
9: voiture n°0: (0m0s0ms)
10: voiture n°0: (0m0s0ms)
11: voiture n°0: (0m0s0ms)
12: voiture n°0: (0m0s0ms)
13: voiture n°0: (0m0s0ms)
14: voiture n°0: (0m0s0ms)
15: voiture n°0: (0m0s0ms)
16: voiture n°0: (0m0s0ms)
17: voiture n°0: (0m0s0ms)
18: voiture n°0: (0m0s0ms)
19: voiture n°0: (0m0s0ms)
20: voiture n°0: (0m0s0ms)
21: voiture n°0: (0m0s0ms)
22: voiture n°0: (0m0s0ms)

希望说得够清楚,希望你能帮到我。
谢谢

您正在使用 System V 信号量,这很……古怪。 SysV 信号量有几个 POSIX 信号量没有的特性,但我发现 POSIX 信号量 API 更容易使用。 (另一方面,我有点喜欢共享内存段的 SysV 风格,尽管它也有一些怪癖。)

只要您使用的是 SysV 信号量,您就应该更加注意 semctl() 的文档。特别是:

The fourth argument is optional and depends upon the operation requested. If required, it is of type union semun, which the application shall explicitly declare:

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short  *array;
} arg;

(POSIX specification for semctl())

当您使用四参数形式尝试设置信号量的初始值时,您传递了一个普通的 int可能 看起来有效,但它不符合规范,因此行为未定义。

此外,由于您打算让 fork() 中的子进程继承您的共享内存段和信号量,而不是独立打开,因此我建议使用密钥 IPC_PRIVATE每个而不是指定应用程序特定的密钥,尽管这与您观察到的不当行为无关。

但我认为你在这里面临的主要问题是你没有做任何事情来确保子进程在主进程检查结果之前完成它们的工作。在这种情况下,实现这一目标的最简单和最自然的方法是主程序,在分叉 所有 子级之后,为每个子级 wait()waitpid()他们。

作为奖励,您应该能够完全摆脱信号量,因为尽管子进程都访问同一个共享内存段,但没有两个访问它的同一部分。因此,它们不需要彼此同步。成功的等待足以确保父级与所有子级正确同步。