C语言fork进程异常:段错误与代码重复执行问题求助
问题描述
我有一项C语言作业,要求根据给定的学生人数(每位学生有期中、期末两门成绩),统计及格学生的数量。需要使用fork()创建指定数量的TA(助教)子进程,每个子进程负责分区判断学生是否及格,TA的数量通过终端传入。
编写代码后遇到以下问题:
- 出现segmentation fault(段错误)
- 添加的打印语句(第53行)执行了(子进程数+父进程)次,尽管它位于
fork()调用之前 - 代码逻辑是创建多个TA进程,每个进程负责一个分区统计及格人数并以该数值作为退出码,父进程通过循环
wait()等待子进程结束并打印其退出码
问题代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/wait.h> #include <unistd.h> void main(int argc, char **argv) { int Number_of_TAs = atoi(argv[1]); int leastgrade = atoi(argv[2]); int size_of_array = 0; int flag = 0; int last_visited = 0; int ind = 0; FILE* ptr; char ch; // Opening file in reading mode ptr = fopen("students.txt", "r"); if (NULL == ptr) { printf("file can't be opened \n"); } do { ch = fgetc(ptr); size_of_array = 2 * ((int)ch - 48); break; } while (ch != EOF); fclose(ptr); int arr_of_grades[size_of_array]; int arr_of_grades2[size_of_array/2]; FILE *myFile; myFile = fopen("students.txt", "r"); int *dummy; fscanf(myFile, "%d", &dummy); for (int i = 0; i < size_of_array; i++) fscanf(myFile, "%d", &arr_of_grades[i]); int index2 = 0; printf("%d", size_of_array); printf(" "); for (int i = 0; i< size_of_array; i=i+2) { arr_of_grades2[index2] = arr_of_grades[i] + arr_of_grades[i+1]; index2 = index2 + 1; } fclose(myFile); int pid, stat_loc; int passed_students = 0; int partion = size_of_array / (2 * Number_of_TAs ); for (int i=0; i< Number_of_TAs; i++) { pid = fork(); ind = ind + partion; } if (pid == 0) { for (int i = ind; i< ind + partion; i++) { if (arr_of_grades2[i]>=leastgrade ) passed_students = passed_students + 1; } exit(passed_students); } else { int sid; for (int i = 0; i<Number_of_TAs; i++) { sid = wait(&stat_loc); if(!(stat_loc & 0x00FF)) printf(stat_loc>>8); printf(" "); } } return 0; }
问题分析与修复
1. 段错误的核心原因
代码中int *dummy; fscanf(myFile, "%d", &dummy);是致命错误:
dummy是未初始化的野指针,&dummy是指针本身的内存地址,而非指针指向的有效内存fscanf会把读取到的数值写入这个非法地址,直接触发段错误
修复:将指针改为普通整数变量:
int dummy; fscanf(myFile, "%d", &dummy);
2. 子进程数量爆炸(打印重复执行的原因)
当前fork()循环逻辑错误:
- 父进程创建子进程后,子进程会继续执行循环剩余迭代,导致创建
2^N个进程(N为TA数量),而非预期的N个 - 额外的子进程会执行后续代码,导致各种逻辑混乱
修复:子进程创建后立即跳出循环,避免继续创建新进程:
for (int i=0; i< Number_of_TAs; i++) { pid = fork(); if (pid == 0) { // 子进程不再参与循环创建新进程 break; } ind = ind + partion; }
3. 分区索引错误
当前ind的累加逻辑会导致子进程拿到错误的分区起始索引,出现分区重叠或越界:
- 父进程每次fork后递增
ind,子进程继承的是累加后的ind值,而非自己负责的分区起始位置
修复:提前计算每个子进程的分区范围,避免共享变量导致的错误:
int total_students = size_of_array / 2; int partion = total_students / Number_of_TAs; int remainder = total_students % Number_of_TAs; // 处理学生数无法被TA数量整除的情况 for (int i=0; i< Number_of_TAs; i++) { int start = i * partion + (i < remainder ? i : remainder); int end = start + partion + (i < remainder ? 1 : 0); pid = fork(); if (pid == 0) { int count = 0; for (int j = start; j < end; j++) { if (arr_of_grades2[j] >= leastgrade) { count++; } } exit(count); } }
4. 其他次要问题修复
void main不符合C标准,改为int main- 文件打开失败后未终止程序,添加
exit(1)避免空指针操作:if (NULL == ptr) { printf("file can't be opened \n"); exit(1); } printf(stat_loc>>8);会将整数当作ASCII字符输出,改为printf("%d ", stat_loc >> 8);- 读取学生数的逻辑脆弱(仅支持个位数),改为用
fscanf读取完整整数:int student_count; fscanf(ptr, "%d", &student_count); size_of_array = student_count * 2;
修复后的完整代码
#include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <unistd.h> int main(int argc, char **argv) { if (argc != 3) { printf("Usage: %s <number_of_tas> <pass_score>\n", argv[0]); exit(1); } int Number_of_TAs = atoi(argv[1]); int leastgrade = atoi(argv[2]); int size_of_array = 0; int total_students = 0; // 读取学生数量 FILE* ptr = fopen("students.txt", "r"); if (NULL == ptr) { printf("file can't be opened \n"); exit(1); } fscanf(ptr, "%d", &total_students); size_of_array = total_students * 2; fclose(ptr); int arr_of_grades[size_of_array]; int arr_of_grades2[total_students]; // 读取成绩数据 FILE *myFile = fopen("students.txt", "r"); if (NULL == myFile) { printf("file can't be opened \n"); exit(1); } int dummy; fscanf(myFile, "%d", &dummy); for (int i = 0; i < size_of_array; i++) { fscanf(myFile, "%d", &arr_of_grades[i]); } // 计算每位学生的总分 for (int i = 0, index2 = 0; i < size_of_array; i += 2, index2++) { arr_of_grades2[index2] = arr_of_grades[i] + arr_of_grades[i+1]; } fclose(myFile); int pid, stat_loc; int partion = total_students / Number_of_TAs; int remainder = total_students % Number_of_TAs; // 创建TA子进程 for (int i = 0; i < Number_of_TAs; i++) { int start = i * partion + (i < remainder ? i : remainder); int end = start + partion + (i < remainder ? 1 : 0); pid = fork(); if (pid == 0) { int passed = 0; for (int j = start; j < end; j++) { if (arr_of_grades2[j] >= leastgrade) { passed++; } } exit(passed); } else if (pid < 0) { printf("Failed to fork process\n"); exit(1); } } // 父进程等待所有子进程并统计结果 int total_passed = 0; for (int i = 0; i < Number_of_TAs; i++) { wait(&stat_loc); if (!(stat_loc & 0x00FF)) { int passed = stat_loc >> 8; total_passed += passed; printf("TA %d counted %d passed students\n", i+1, passed); } } printf("Total passed students: %d\n", total_passed); return 0; }
内容的提问来源于stack exchange,提问作者Mohamed Mohsen
相关产品推荐
相关产品推荐

