进程似乎没有减少信号量

时间:2016-12-21 14:10:45

标签: c process semaphore

我的c程序出了问题 我已经在这里写了一个关于同一个程序的主题(我遇到了fork的问题):https://stackoverflow.com/questions/41132029/wrong-data-output-when-forking

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

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

例如:

  • 对于大多数人来说,Pilote数字是不正确的(很多都是n°0)。
  • 他们都有不切实际的最佳时间(0分0秒0毫秒)。

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

这是“让比赛”的节目:

/* /!\ 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
            // https://stackoverflow.com/questions/8623131/why-is-rand-not-so-random-after-fork
            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)

希望它足够清楚,你将能够帮助我 感谢。

1 个答案:

答案 0 :(得分:2)

你正在使用System V信号量,这是......古怪的。 SysV信号量有一些POSIX信号量没有的功能,但我发现POSIX信号量API更容易使用。 (另一方面,我有点像共享内存段的SysV样式,即使它也有一些怪癖。)

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

  

第四个参数是可选的,取决于操作   请求。如果需要,它的类型为union semun,其中   申请应明确声明:

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

POSIX specification for semctl()

当您使用四个arg表单尝试设置信号量的初始值时,您将传递一个普通的int可能似乎可以正常工作,但它不符合规定,因此行为未定义。

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

但我认为你在这里遇到的主要问题是你没有做任何事情来确保子进程在主进程检查结果之前完成他们的工作。在这种情况下,实现这一目标的最简单,最自然的方法是,在分配所有子项后,主程序为每个<{1}}或wait()。< / p>

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