我的c程序出了问题 我已经在这里写了一个关于同一个程序的主题(我遇到了fork的问题):https://stackoverflow.com/questions/41132029/wrong-data-output-when-forking
我正在模拟F1练习。现在,我有一些不相关的数据(1/3的车有错误的时间(0min0s0ms)。
我认为这是一个并发问题所以我试图实现一个信号量。
这样做的目的是一次只允许一个进程写入共享内存。
但现在,我的所有输出数据都是错误的
例如:
我注意到的一件事是,如果我删除增加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)
希望它足够清楚,你将能够帮助我 感谢。
答案 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>
作为奖励,您应该能够完全摆脱信号量,因为尽管子进程处理所有访问相同的共享内存段,但没有两个访问它的相同部分。因此,它们不需要彼此同步。成功的等待将足以确保父母与所有孩子正确同步。