Reputation: 307
I've got a problem with my c program.
I'm simulating an F1 practice. Whithout fork, I've got relevant data. But when I fork to get 22 processes, I get irrelevant data.
For instance:
Here's the program that "make the race" (without the fork):
#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
//sem_t semaph;
float ranf() { // PRNG pour des 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, écart-type s */
float x1, x2, w, y1, result;
static float y2;
static int use_last = 0;
if (use_last) /* use the value of the 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 number between min and max
}
int genRaceEvents(const int max) { // Decide the race events
return rand() % max; // On génère le nombre entre 0 et max - 1;
}
int compareBest(const void *p1, const void *p2) { // Comparison method for 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) { // Comparison method for total time (full race)
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) {
//sem_wait(&semaph);
/* Instanciation of all the values (except pilote_id) */
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;
//printf("START => n° %d\n", p->best);
//printf("%d\n", p->totalTime);
//pause();
for (int i = 0; i < MAX_TOURS; i++) { // For every lap
p->isPit = 0; // Beginning of lap, the pilote does not pit
if (!(p->hasGivenUp)) { // If the pilote didn't give up
int givingUpEvent = genRaceEvents(500); // Generate number between 1 and 499
//printf("// %d //\n", givingUpEvent);
if (givingUpEvent == 14 && strcmp(name, "Race") == 0) { // If the pilote gave up during race
//printf("abd ? => %d\n", givingUpEvent);
p->best = 3 * 60 * 3600;
//p->hasGivenUp = 1;
p->hasGivenUpDuringRace = 1;
return 0; // Stop le pilote
}
else if (givingUpEvent == 14) { // If the pilote gave up (But not during race (maybe practices or qualifications)
//printf("abd ? => %d\n", givingUpEvent);
p->best = 3 * 60 * 3600 + 3;
p->hasGivenUp = 1;
return 0; // Stop le pilote
}
}
if (p->numberOfPits < 2) { // Max 2 pit stop
p->isPit = genRaceEvents(2); // Generate number between 0 and 1
if (p->isPit) {
p->numberOfPits++;
if ((strcmp(name, "Practices") == 0)|| (strcmp(name, "Qualifs") == 0)) continue; // Next iteration (= next lap)
}
}
// Otherwise we can do a lap
int S1 = 0.275 * (103000 + randGaussien(5000, 2000)); // portion of the lap * Gausse curve (= min time + fun(médian, écart-type))
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 in race and the pilote pit
S1 += genTime(20 * 3600, 25 * 3600); // We add between 20 and 25 sec at the sector 1
}
p->s1 = S1; // We save S1 time (S1 = Sector 1)
p->s2 = S2; // We save S2 time
p->s3 = S3; // etc...
int lap = S1 + S2 + S3; // Lap time
if (p->bestS1 > S1) p->bestS1 = S1; // If it is its best S1 time, we save it
if (p->bestS2 > S2) p->bestS2 = S2; // If it is its best S2
if (p->bestS3 > S3) p->bestS3 = S3; // If it is its best S3
if (p->best > lap) p->best = lap; // If it is its best lap time, we save it
if ((strcmp(name, "Race") == 0)) {
p->totalTime += lap;
}
} // End of loop
//printf("END => n° %d\n", p->best);
}
int main(int argc, char const *argv[]) {
//printf("hello\n");
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 pour la course
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}; // Array that conain the pilote ids
struct Pilote Q2[16]; // Array of pilotes for Q2
struct Pilote Q3[10]; // Array of pilotes for Q3
struct Pilote mainRun[MAX_PILOTES]; // Array of pilotes for the other shows
struct Pilote pilotesTab[22];
pid_t tabPID[MAX_PILOTES];
int j;
for (j = 0; j < MAX_PILOTES; j++) { /* Loop: 22 pilotes */
pilotesTab[j].pilote_id = pilotes_numbers[j]; // Init the pilote id
run(&pilotesTab[j], "Practices"); // Launch the pratices for the current pilote
}
printf("==================================================== \n");
fillTab(mainRun, pilotesTab, 0, MAX_PILOTES); // Fill the mainRun Array before sorting and showing the results (will be useful for shared memory)
showResults(mainRun, MAX_PILOTES, "Practices"); // show the results
printf("====================================================\n");
return 0;
}
Here's its output(correct) :
1: voiture n°26: (1m44s239ms)
2: voiture n°11: (1m44s503ms)
3: voiture n°33: (1m44s587ms)
4: voiture n°55: (1m44s672ms)
5: voiture n°20: (1m44s720ms)
6: voiture n°12: (1m44s864ms)
7: voiture n°7: (1m45s87ms)
8: voiture n°77: (1m45s136ms)
9: voiture n°8: (1m45s257ms)
10: voiture n°21: (1m45s383ms)
11: voiture n°14: (1m45s553ms)
12: voiture n°94: (1m45s555ms)
13: voiture n°27: (1m45s702ms)
14: voiture n°30: (1m45s731ms)
15: voiture n°9: (1m45s771ms)
16: voiture n°31: (1m45s792ms)
17: voiture n°3: (1m45s835ms)
18: voiture n°5: (1m45s862ms)
19: voiture n°22: (1m45s907ms)
20: voiture n°44: (1m46s212ms)
21: voiture n°6: (1m46s390ms)
22: voiture n°19: Abandon
And now the programm that "make the race" (with the fork now).
Only the for (... ; j < MAX_PILOTES ; ...) loop (in main) has changed:
pid_t tabPID[MAX_PILOTES];
int j;
for (j = 0; j < MAX_PILOTES; j++) { /* Creation of 22 processes */
tabPID[j] = fork();
if (tabPID[j] == -1) { // Error
printf("Erreur lors du fork()\n");
return 0;
}
if (tabPID[j] == 0) { // Fils
pilotesTab[j].pilote_id = pilotes_numbers[j]; // Init pilote id
//printf("PILOTE ID: %d\n", pilotesTab[j].pilote_id); // OK
//waitpid(tabPID[j], NULL, 0);
run(&pilotesTab[j], "Practices");
exit(0);
} else {
/* Nothing */
}
} /* End of the 22 processes */
And its buggy output:
1: voiture n°1: Abandon
2: voiture n°32765: (0m0s0ms)
3: voiture n°32593: Abandon
4: voiture n°888005824: Abandon
5: voiture n°32593: Abandon
6: voiture n°1700966438: Abandon
7: voiture n°4196464: Abandon
8: voiture n°0: (0m0s0ms)
9: voiture n°878665720: Abandon
10: voiture n°16220219: Abandon
11: voiture n°885803424: Abandon
12: voiture n°885789819: Abandon
13: voiture n°46: Abandon
14: voiture n°887994784: (0m32s765ms)
15: voiture n°0: Abandon
16: voiture n°32593: Abandon
17: voiture n°32593: Abandon
18: voiture n°0: Abandon
19: voiture n°0: (13193m41s423ms)
20: voiture n°32765: Abandon
21: voiture n°32765: Abandon
22: voiture n°0: (32546m42s655ms)
Eventually, the code that show the results (in another .c file) :
#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) {
qsort(tab, nbElems, sizeof(Pilote), compareTot);
for (int k = 0; k < nbElems; k++) {
if (tab[k].hasGivenUpDuringRace || tab[k].best == 3 * 60 * 3600 + 3) {
printf("%d: voiture n°%d: Abandon\n", k+1, tab[k].pilote_id);
continue;
}
printf(
"%d%s%d%s%d%s%d%s%d%s\n" ,k+1,
": voiture n°", tab[k].pilote_id,
": (", tab[k].best/60000, "m",
tab[k].best / 1000 % 60, "s",
tab[k].best-(tab[k].best/1000)*1000, "ms)"
);
}
} else {
for (int k = 0; k < nbElems; k++) {
qsort(tab, nbElems, sizeof(Pilote), compareBest);
if (tab[k].hasGivenUpDuringRace || tab[k].best == 3 * 60 * 3600 + 3) {
printf("voiture n°%d: Abandon\n", tab[k].pilote_id);
continue;
}
printf(
"%d%s%d%s%d%s%d%s%d%s\n" ,k+1,
": voiture n°", tab[k].pilote_id,
": (", tab[k].totalTime/60000,"m",
(tab[k].totalTime/1000)%60,"s",
tab[k].totalTime-(tab[k].totalTime/1000)*1000,"ms)"
);
}
}
}
So, my question is, Why does it happen ? And how to fix that ?
Thanks.
I've set up a shared memory but now I have 2 problems :
- I don't know when and where should I detach and remove the SM segment.
- All the cars have the same time (ex: 1m45s908ms).
Here's the code implemented (for Shared Memory):
struct Pilote *pilotesTab; // pointer to SM. Instead of a simple array of struct as before
pid_t tabPID[MAX_PILOTES];
int shmid = 0;
key_t key;
/**
* Set up shared memory
*/
// Key generation for shared memory
key = ftok(argv[0], 123); // argv[O] => nom du programme lancé, ID (char)
// Initialisation of shared memory
shmid = shmget(key, MAX_PILOTES * sizeof(Pilote), IPC_CREAT | 0644);
if (shmid == -1) {
printf("Erreur lors de l'allocation de la shared memory.");
return 0;
}
// Attach the shared memory
pilotesTab = shmat(shmid, NULL, 0);
/**
* Fork (The same code than before)
*/
int j;
for (j = 0; j < MAX_PILOTES; j++) { /* Création des 22 processus */
tabPID[j] = fork();
if (tabPID[j] == -1) { // Erreur
printf("Erreur lors du fork()\n");
return 0;
}
if (tabPID[j] == 0) { // Fils
pilotesTab[j].pilote_id = pilotes_numbers[j]; // Initialise le numéro du pilote
//printf("PILOTE ID: %d\n", pilotesTab[j].pilote_id); // OK
run(&pilotesTab[j], "Practices");
exit(0);
} else {
waitpid(tabPID[j], NULL, 0);
//shmdt(pilotesTab);
//shmctl(shmid, IPC_RMID, 0);
}
} /* Fin des 22 processus */
printf("==================================================== \n");
fillTab(mainRun, pilotesTab, 0, MAX_PILOTES); // Fill the tab (mainRun) with the data from the SM (before sorting because we can't sort SM).
showResults(mainRun, MAX_PILOTES, "Practices");
printf("====================================================\n");
And here's the brand new buggy output:
1: voiture n°44: (1m44s908ms)
2: voiture n°6: (1m44s908ms)
3: voiture n°5: (1m44s908ms)
4: voiture n°7: (1m44s908ms)
5: voiture n°3: (1m44s908ms)
6: voiture n°33: (1m44s908ms)
7: voiture n°19: (1m44s908ms)
8: voiture n°77: (1m44s908ms)
9: voiture n°11: (1m44s908ms)
10: voiture n°27: (1m44s908ms)
11: voiture n°26: (1m44s908ms)
12: voiture n°55: (1m44s908ms)
13: voiture n°14: (1m44s908ms)
14: voiture n°22: (1m44s908ms)
15: voiture n°9: (1m44s908ms)
16: voiture n°12: (1m44s908ms)
17: voiture n°20: (1m44s908ms)
18: voiture n°30: (1m44s908ms)
19: voiture n°8: (1m44s908ms)
20: voiture n°21: (1m44s908ms)
21: voiture n°31: (1m44s908ms)
22: voiture n°94: (1m44s908ms)
I don't think it's a problem of competition. Is it ?
Upvotes: 1
Views: 109
Reputation: 1458
OK so I am not a fork
expert, but here's my diagnosis
When you fork, your whole environment is cloned.
This means that whatever you do in the child process has no effect on what happens in the parent process. Here, you should print the data from the child process, because your pilotes
tab
in the parent process is entirely unaffected by the race.
Your print is "buggy" because all the values you are printing are not initialized. You are literally printing whatever was in the stack when you declared the table.
moreover, since tabPID[j] == 0
you are calling waitpid(0, 0, 0)
which, quoting the man wait(2)
meaning wait for any child process whose process group ID is equal to that of the calling process.
Since that is called IN the child process, you are waiting for signals from the children of your child process, which I believe are non-existent.
You mignt want to call that in the else
Compiling the recent comments in one answer (for other users):
A good way to access data in a forked program is to set up a shared memory (using shmget
).
The second problem raised by a fork is the random generation (evoked in This question), since srand is called only in the parent process, all the children have the same random seed, thus the same results. The answer there works: calling srand(time(NULL) ^ (getpid()<<16))
in the child processes
Upvotes: 3