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

使用C语言fork多进程统计文件总行数结果异常的解决方法

多进程统计文件总行数的问题与解决方法

当前代码通过fork创建多进程统计文件行数,但最终统计的总行数与实际不符,同时子进程输出的行数累加后和显示的总行数差异极大,核心问题有两点:

  1. 所有子进程都将文件指针重置到开头,导致每个子进程都重复统计了整个文件的行数;
  2. 使用exit(lines)传递行数时,WEXITSTATUS只能获取退出状态的低8位(范围0-255),当行数超过255时会被截断,导致累加结果错误。

原代码

char *filename = "/home/septian/Documents/Repository.cs";
FILE *file = fopen(filename, "r");

if (file == NULL) {
    perror("File open failed");
    return 1;
}

int num_processes = 4; // Number of child processes to create
pid_t pid[num_processes];

for (int i = 0; i < num_processes; i++) {
    pid_t child_pid = fork();

    if (child_pid < 0) {
        perror("Fork failed");
        return 1;
    } else if (child_pid == 0) {
        // This code runs in the child process
        int lines = 0;
        char ch;
        fseek(file, 0, SEEK_SET); // Rewind the file to the beginning

        // Read the file and count lines
        while ((ch = fgetc(file)) != EOF) {
            if (ch == '\n') {
                lines++;
            }
        }

        printf("Child process %d counted %d lines\n", i, lines);
        exit(lines); // Terminate the child process with the line count
    } else {
        pid[i] = child_pid; // Store child process IDs in an array
    }
}

// This code runs in the parent process
printf("parent process\n");
int total_lines = 0;
for (int i = 0; i < num_processes; i++) {
    int status;
    waitpid(pid[i], &status, 0); // Wait for child processes to finish
    total_lines += WEXITSTATUS(status);
}

printf("Total lines in the file: %d\n", total_lines);
fclose(file);

错误输出

parent process
Child process 1 counted 15340 lines
Child process 3 counted 8866 lines
Child process 2 counted 5361 lines
Child process 0 counted 7089 lines
Total lines in the file: 816

正确实现思路与代码

核心改进点

  1. 分割文件区间:先获取文件总大小,将文件划分为多个连续区间,每个子进程负责统计一个区间内的行数,避免重复统计;
  2. 安全传递结果:使用管道(pipe)在父子进程间传递统计结果,替代exit传递,避免数值截断问题;
  3. 处理边界情况:解决换行符跨区间、文件最后一行无换行符的统计遗漏问题。

修改后的代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>

int main() {
    char *filename = "/home/septian/Documents/Repository.cs";
    FILE *file = fopen(filename, "r");
    if (file == NULL) {
        perror("File open failed");
        return 1;
    }

    // 获取文件大小
    struct stat file_stat;
    if (fstat(fileno(file), &file_stat) == -1) {
        perror("Failed to get file stats");
        fclose(file);
        return 1;
    }
    off_t file_size = file_stat.st_size;
    int num_processes = 4;
    off_t chunk_size = file_size / num_processes;
    int pipes[num_processes][2]; // 每个子进程对应一个管道

    for (int i = 0; i < num_processes; i++) {
        // 创建管道
        if (pipe(pipes[i]) == -1) {
            perror("Pipe creation failed");
            fclose(file);
            return 1;
        }

        pid_t child_pid = fork();
        if (child_pid < 0) {
            perror("Fork failed");
            fclose(file);
            return 1;
        } else if (child_pid == 0) {
            // 子进程:关闭读端,只写
            close(pipes[i][0]);
            off_t start = i * chunk_size;
            off_t end = (i == num_processes - 1) ? file_size : (i + 1) * chunk_size;

            // 定位到区间起始位置
            if (fseek(file, start, SEEK_SET) == -1) {
                perror("Fseek failed in child");
                exit(1);
            }

            int lines = 0;
            char ch;
            // 处理区间起始前的换行:如果前一个字符是换行,当前区间需要计数
            if (start > 0) {
                fseek(file, start - 1, SEEK_SET);
                ch = fgetc(file);
                if (ch == '\n') {
                    lines++;
                }
                fseek(file, start, SEEK_SET);
            }

            // 统计当前区间内的换行
            while (ftell(file) < end && (ch = fgetc(file)) != EOF) {
                if (ch == '\n') {
                    lines++;
                }
            }

            // 处理文件最后一行(无换行符的情况)
            if (i == num_processes - 1 && ch == EOF) {
                if (file_size > 0) {
                    fseek(file, -1, SEEK_END);
                    ch = fgetc(file);
                    if (ch != '\n') {
                        lines++;
                    }
                }
            }

            printf("Child process %d counted %d lines\n", i, lines);
            // 通过管道发送结果
            write(pipes[i][1], &lines, sizeof(lines));
            close(pipes[i][1]);
            fclose(file);
            exit(0);
        } else {
            // 父进程:关闭写端,只读
            close(pipes[i][1]);
        }
    }

    // 父进程汇总结果
    printf("parent process\n");
    int total_lines = 0;
    for (int i = 0; i < num_processes; i++) {
        int lines;
        read(pipes[i][0], &lines, sizeof(lines));
        total_lines += lines;
        close(pipes[i][0]);
        // 等待子进程结束
        wait(NULL);
    }

    printf("Total lines in the file: %d\n", total_lines);
    fclose(file);
    return 0;
}

代码说明

  • 文件分割:根据进程数将文件划分为均等的块,最后一个进程负责剩余的所有内容,避免重复统计;
  • 管道通信:每个子进程通过管道将统计的行数发送给父进程,摆脱了exit状态的数值范围限制;
  • 边界处理:检查区间起始位置的前一个字符是否为换行,避免漏统计;同时处理文件最后一行无换行符的情况,确保总行数准确。

内容的提问来源于stack exchange,提问作者Septian Ibn Yohan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 23:06:31