多进程C语言锦标赛程序日志重复输出异常排查求助
问题描述
我编写了一个模拟完整锦标赛的C程序,需将结果写入日志文件。每个赛事阶段的每场比赛由单独子进程(通过fork创建)处理。父进程等待所有子进程结束后,通过消息队列获取结果并写入日志,但实际日志多次重复输出八分之一决赛等赛事段内容(如重复8次八分之一决赛结尾标识),与预期的按阶段依次输出不符。
核心代码
/**data preparation and stuff**/ for(int i = 0; i < nbMatch; i++){ int pid = fork(); if(pid == -1){ perror("Error occured while forking\n"); return 1; }else if(pid == 0){ srand(getpid()); Team t; if(mode == 2){ P(mutId); t = match(tabMatch[i],mode,fx); V(mutId); } else{ t = match(tabMatch[i],mode,fx); } Message buf; if((msgsend(msgid, buf, teamName(t), strlen(teamName(t)))) == -1){ perror("Error occured while putting a message in the queue, shutting down\n"); return 4; } exit(0); } } while((wpid = wait(&status)) > 0);//waiting for every children processes to die. Message buffer; char *res; for(int i = 0; i < nbMatch; i++){ res = msgrecv(msgid,buffer); Team t = getTeam(b,res); Team the = getOponent(tabMatch, (*t).name, nbMatch); fprintf(fx,"Match numéro %d du %d de finale: Perdant : %s , Gagnant : %s\n",i+1, nbMatch, t->name, the->name); b = suppTeam(b,t); } fprintf(fx,"---FIN DU %d DE FINALE---\n", nbMatch); nbMatch = nbMatch / 2; if(nbMatch == 0){ mutfree(mutId); finBracket = 1; printf("\33[1;32mThe grand winner is : %s \33[0m\n",teamName(b->team)); }else{ printf("\nEnd of the segment\n\n\n"); } /*Other stuff*/
期望输出
Match numéro 1 du 8 de finale: Perdant : Orange , Gagnant : famas viseur holo chargeur grande capacité Match numéro 2 du 8 de finale: Perdant : Condeflan , Gagnant : Quiperd Match numéro 3 du 8 de finale: Perdant : Sertgille , Gagnant : Pau Match numéro 4 du 8 de finale: Perdant : ekip , Gagnant : griffondor Match numéro 5 du 8 de finale: Perdant : BoisBubulle , Gagnant : denis Match numéro 6 du 8 de finale: Perdant : Antredonc , Gagnant : Nan Match numéro 7 du 8 de finale: Perdant : Free , Gagnant : L'ile Match numéro 8 du 8 de finale: Perdant : FC bretagne , Gagnant : Brusxsscsel ---FIN DU 8 DE FINALE--- Match numéro 1 du 4 de finale: Perdant : Pau , Gagnant : griffondor Match numéro 2 du 4 de finale: Perdant : Brusxsscsel , Gagnant : Quiperd Match numéro 3 du 4 de finale: Perdant : L'ile , Gagnant : denis Match numéro 4 du 4 de finale: Perdant : Nan , Gagnant : famas viseur holo chargeur grande capacité ---FIN DU 4 DE FINALE--- [...]---FIN DU 2 DE FINALE---
实际输出
Match numéro 1 du 8 de finale: Perdant : Orange , Gagnant : famas viseur holo chargeur grande capacité Match numéro 2 du 8 de finale: Perdant : Condeflan , Gagnant : Quiperd Match numéro 3 du 8 de finale: Perdant : Sertgille , Gagnant : Pau Match numéro 4 du 8 de finale: Perdant : ekip , Gagnant : griffondor Match numéro 5 du 8 de finale: Perdant : BoisBubulle , Gagnant : denis Match numéro 6 du 8 de finale: Perdant : Antredonc , Gagnant : Nan Match numéro 7 du 8 de finale: Perdant : Free , Gagnant : L'ile Match numéro 8 du 8 de finale: Perdant : FC bretagne , Gagnant : Brusxsscsel ---FIN DU 8 DE FINALE--- Match numéro 1 du 4 de finale: Perdant : Pau , Gagnant : griffondor Match numéro 2 du 4 de finale: Perdant : Brusxsscsel` , Gagnant : Quiperd` Match numéro 3 du 4 de finale: Perdant : L'ile` , Gagnant : denis Match numéro 4 du 4 de finale: Perdant : Nan` , Gagnant : famas viseur holo chargeur grande capacité ---FIN DU 4 DE FINALE--- Match numéro 1 du 8 de finale: Perdant : Orange` , Gagnant : famas viseur holo chargeur grande capacité Match numéro 2 du 8 de finale: Perdant : Condeflan` , Gagnant : Quiperd` Match numéro 3 du 8 de finale: Perdant : Sertgille , Gagnant : Pau Match numéro 4 du 8 de finale: Perdant : ekip , Gagnant : griffondor Match numéro 5 du 8 de finale: Perdant : BoisBubulle` , Gagnant : denis Match numéro 6 du 8 de finale: Perdant : Antredonc` , Gagnant : Nan` Match numéro 7 du 8 de finale: Perdant : Free` , Gagnant : L'ile` Match numéro 8 du 8 de finale: Perdant : FC bretagne , Gagnant : Brusxsscsel` ---FIN DU 8 DE FINALE--- Match numéro 1 du 4 de finale: Perdant : Pau , Gagnant : griffondor Match numéro 2 du 4 de finale: Perdant : Brusxsscsel` , Gagnant : Quiperd` Match numéro 3 du 4 de finale: Perdant : L'ile` , Gagnant : denis Match numéro 4 du 4 de finale: Perdant : Nan` , Gagnant : famas viseur holo chargeur grande capacité ---FIN DU 4 DE FINALE--- [till the eof...]
问题原因及解决方案
1. 消息队列结构未正确初始化,导致消息类型混乱
System V消息队列要求消息结构的第一个成员必须是long mtype(消息类型),用于区分不同消息。你的代码中Message buf是未初始化的自动变量,栈上的垃圾值会导致mtype随机,父进程读取时会混入旧阶段的残留消息或乱序消息,进而重复输出旧阶段内容。
解决办法:
- 定义规范的消息结构:
typedef struct { long mtype; // 必须是第一个成员 char mtext[256]; // 存储队伍名称,预留足够空间 } Message; - 子进程发送消息时初始化
mtype为固定值(比如1,统一标识比赛结果消息),并确保字符串终止:Message buf; buf.mtype = 1; strncpy(buf.mtext, teamName(t), sizeof(buf.mtext)-1); buf.mtext[sizeof(buf.mtext)-1] = '\0'; // 强制添加终止符 if(msgsnd(msgid, &buf, strlen(buf.mtext)+1, 0) == -1) { perror("msgsnd failed"); exit(4); } - 父进程接收时指定读取
mtype=1的消息:res = msgrecv(msgid, &buffer, sizeof(buffer.mtext), 1);
2. 字符串未正确终止,输出乱码
实际输出中的多余符号,是因为teamName(t)返回的字符串没有\0终止符,导致strlen`计算错误,写入消息队列时混入栈垃圾字符。
解决办法:
- 确保
teamName(t)返回的字符串自带终止符,或者手动复制时添加:char team_name[256]; strncpy(team_name, teamName(t), sizeof(team_name)-1); team_name[sizeof(team_name)-1] = '\0';
3. 消息队列未在阶段结束后清理
如果某阶段的消息未被完全读取,残留消息会被下一阶段父进程读取,导致重复输出旧内容。
解决办法:
- 每个阶段结束后,清空消息队列剩余内容:
Message temp_buf; // 非阻塞读取剩余消息,直到队列为空 while(msgrcv(msgid, &temp_buf, sizeof(temp_buf.mtext), 1, IPC_NOWAIT) != -1) {}
4. 锦标赛阶段推进逻辑可能出错
检查suppTeam(b,t)和nbMatch的更新逻辑,若b未正确更新,会导致nbMatch未被正确减半,大循环重复执行同一阶段。
内容的提问来源于stack exchange,提问作者Djolalo
相关产品推荐
相关产品推荐

