You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多进程程序无法写入第二个文件的原因及修复方法咨询

问题描述

我编写了一个程序用于处理最多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
错误原因分析
  1. 函数参数类型不匹配:create_output函数的参数声明为pid_t* temp_processes[](二级指针),但调用时传入的是一维数组temp_processes(一级指针)。这种类型不匹配导致函数内部访问数组元素时读取到错误的内存值,出现了无效的PID 0。
  2. 文件读取逻辑错误:使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 03:55:55