多进程程序无法写入第二个文件的原因及修复方法咨询
问题描述
我编写了一个程序用于处理最多10个包含学生姓名及成绩的.txt文件,逻辑如下:
- 为每个输入文件创建独立子进程,计算学生平均成绩后写入以进程PID命名的临时文件
- 父进程等待所有子进程完成后,创建新进程将所有临时文件合并为一个输出文件
但运行两个输入文件时,始终只有一个文件处理成功,另一个会缺失PID,导致程序尝试读取不存在的0.temp文件,最终输出缺失第二个文件的学生信息。
控制台输出
0 = 35222 1 = 0 end 0 = 35222 1 = 35223 end process: 35223 file: gr_2.txt number of students: 2 process: 35222 file: gr_1.txt number of students: 3 grade calculation for 3 students is done Could not open temporary file 0.temp
输入文件示例
file1.txt:
Abraham 80 90 75 Benny 90 Garland 70 9 90 100
file2.txt:
Dana 90 95 Ron 100 80 90
当前输出(缺失内容)
Abraham 81.7 Benny 90.0 Garland 67.2
错误原因分析
- 函数参数类型不匹配:
create_output函数的参数声明为pid_t* temp_processes[](二级指针),但调用时传入的是一维数组temp_processes(一级指针)。这种类型不匹配导致函数内部访问数组元素时读取到错误的内存值,出现了无效的PID 0。 - 文件读取逻辑错误:使用
while (!feof(fp_in))作为循环条件会导致最后一条记录被重复读取(feof仅在读取失败后才会置位),不过这不是当前缺失数据的直接原因,但会引发潜在问题。
修复方案
1. 修正create_output的参数类型
将函数声明改为接受一维数组(或一级指针):
void create_output(int file_count, pid_t temp_processes[])
或等价的:
void create_output(int file_count, pid_t* temp_processes)
这样参数类型与调用时传入的数组匹配,确保能正确读取所有子进程的PID。
2. 修复文件读取的循环逻辑
替换feof的错误用法,改为先尝试读取姓名,成功后再处理成绩:
// 原错误逻辑 // while (!feof(fp_in)) { // fscanf(fp_in, "%s", students[num_students].name); // ... // } // 修正后逻辑 while (fscanf(fp_in, "%s", students[num_students].name) == 1) { int grade; students[num_students].num_grades = 0; while (fscanf(fp_in, "%d", &grade) == 1) { students[num_students].grades[students[num_students].num_grades] = grade; students[num_students].num_grades++; } num_students++; }
3. 可选:移除调试打印代码
删除原代码中的调试打印语句,避免干扰正常输出:
// printf("%d = %d\n",0,temp_processes[0]); // printf("%d = %d\nend\n",1,temp_processes[1]);
修改后的完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #define GRADES_FILE "all_std.log" typedef struct { char name[11]; int grades[256]; int num_grades; } student_t; void report_data_summary(int num_stud) { fprintf(stderr, "grade calculation for %d students is done\n", num_stud); } void calculate_students_grade_average_from_input(int file_number, char* filenames[]) { student_t students[256]; int num_students = 0; char filename[50]; sprintf(filename, "%d.temp", getpid()); FILE* fp_in = fopen(filenames[file_number], "r"); if (fp_in == NULL) { printf("Could not open file %s\n", filenames[file_number]); exit(EXIT_FAILURE); } // 修正feof循环逻辑 while (fscanf(fp_in, "%s", students[num_students].name) == 1) { int grade; students[num_students].num_grades = 0; while (fscanf(fp_in, "%d", &grade) == 1) { students[num_students].grades[students[num_students].num_grades] = grade; students[num_students].num_grades++; } num_students++; } fclose(fp_in); FILE* fp_out = fopen(filename, "w"); if (fp_out == NULL) { printf("Could not open output file %s\n", filename); return; } for (int j = 0; j < num_students; ++j) { int sum = 0; for (int k = 0; k < students[j].num_grades; ++k) { sum += students[j].grades[k]; } float avg = (float)sum / students[j].num_grades; fprintf(fp_out, "%s %.1f\n", students[j].name, avg); } fclose(fp_out); fprintf(stderr, "process: %d file: %s number of students: %d\n", getpid(), filenames[file_number], num_students); } // 修正参数类型 void create_output(int file_count, pid_t temp_processes[]) { int total_students = 0; pid_t pid = fork(); if (pid < 0) { perror("fork failed"); exit(EXIT_FAILURE); } else if (pid == 0) { FILE* fp_final = fopen(GRADES_FILE, "w"); if (fp_final == NULL) { perror("Could not open final output file"); exit(EXIT_FAILURE); } for (int i = 0; i < file_count; ++i) { char temp_filename[16]; sprintf(temp_filename, "%d.temp", temp_processes[i]); FILE* fp_temp = fopen(temp_filename, "r"); if (fp_temp == NULL) { printf("Could not open temporary file %s\n",temp_filename); return; } char line[101]; while (fgets(line, sizeof(line), fp_temp)) { fputs(line, fp_final); total_students++; } report_data_summary(total_students); fclose(fp_temp); } fclose(fp_final); return; } else { wait(NULL); } } void ex01(int file_count, char* filenames[]) { pid_t temp_processes[10]; pid_t pid; for (int i = 1; i < file_count; ++i) { pid = fork(); if (pid < 0) { perror("fork failed"); exit(EXIT_FAILURE); } else if (pid > 0) { temp_processes[i-1] = pid; // 移除调试打印 // printf("%d = %d\n",0,temp_processes[0]); // printf("%d = %d\nend\n",1,temp_processes[1]); } else { calculate_students_grade_average_from_input(i, filenames); return; } } for (int i = 1; i < file_count; ++i) { wait(NULL); } if (pid > 0) { create_output(file_count - 1, temp_processes); } } int main(int argc, char* argv[]) { ex01(argc, argv); return 0; }
验证结果
修复后运行程序,两个子进程的临时文件都会被正确读取,最终输出文件all_std.log会包含所有学生的信息:
Abraham 81.7 Benny 90.0 Garland 67.2 Dana 92.5 Ron 90.0
内容的提问来源于stack exchange,提问作者SiiilverSurfer
相关产品推荐
相关产品推荐

