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

多进程下重定向文件到stdin使用fgets读取行的异常问题

问题分析

你的问题核心在于fork后所有进程共享同一个stdin的文件偏移量和stdio缓冲区,具体原因如下:

  1. 当stdin重定向到文件时,C标准库默认使用全缓冲(手动输入时是行缓冲),fork后所有子进程会继承父进程已经填充的stdio缓冲区。
  2. 父进程执行fgets读取第一行后,文件偏移量已经移动到下一行,同时stdio缓冲区中可能已经缓存了文件后续内容。子进程继承这个缓冲区和偏移量,再次调用fgets时会读取缓冲区中的残留数据,或者因为偏移量混乱导致读取到错误内容(也就是你看到的乱码)。
  3. 当前代码中每个进程只读取一次就退出,且没有任何同步机制,多个进程同时读取会互相干扰。
解决方案

下面是修正后的代码,解决了上述问题:

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

#define BUFFERSIZE 1024

int main(int argc, char** argv) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <num-processes>\n", argv[0]);
        exit(1);
    }

    int maxpc = atoi(argv[1]);
    // 设置stdin为行缓冲,避免全缓冲导致fork后缓冲区共享问题
    setvbuf(stdin, NULL, _IOLBF, 0);

    printf("Main process %x\n", getpid());
    pid_t pid;
    // 创建子进程:父进程负责创建所有子进程,子进程直接跳出循环
    for (int pc = 0; pc < maxpc; pc++) {
        pid = fork();
        if (pid < 0) {
            perror("fork failed");
            exit(1);
        } else if (pid == 0) {
            break;
        } else {
            printf("p%x: Created child process %x\n", getpid(), pid);
        }
    }

    // 每个进程循环读取直到EOF
    char command[BUFFERSIZE];
    while (fgets(command, BUFFERSIZE, stdin) != NULL) {
        // 去掉换行符,避免输出重复换行
        command[strcspn(command, "\n")] = '\0';
        printf("p%x: Read %s\n", getpid(), command);
        // 此处可添加自定义处理逻辑
    }

    // 父进程等待所有子进程结束,子进程无需wait
    if (pid != 0) {
        int status;
        while (wait(&status) > 0);
    }

    exit(0);
}
关键修改点
  • 设置行缓冲:通过setvbuf(stdin, NULL, _IOLBF, 0)将stdin设置为行缓冲,确保每次读取一行后缓冲区会被刷新,避免fork后子进程继承残留的缓冲数据。
  • 修正子进程创建逻辑:原代码的链式fork逻辑会导致子进程再创建子进程,修正后父进程单独负责创建所有子进程,子进程直接进入读取逻辑。
  • 循环读取直到EOF:让每个进程持续读取stdin直到文件结束,而非仅读取一次。
  • 父进程等待所有子进程:原代码的wait(NULL)仅能等待一个子进程,改为循环等待直到所有子进程退出。
预期运行效果

执行./a.out 3 < batchcmds后,进程会交替读取文件中的行(顺序由进程调度决定),不会出现乱码:

~$ ./a.out 3 < batchcmds 
Main process 35e2e2
p35e2e2: Created child process 35e2e3
p35e2e2: Created child process 35e2e4
p35e2e2: Created child process 35e2e5
p35e2e2: Read ./testprog 1 2
p35e2e3: Read ./testprog 3 1
p35e2e4: Read ./testprog 2 2
p35e2e5: Read ./testprog 1 1
p35e2e2: Read ./testprog 2 1
p35e2e3: Read ./testprog 1 3
p35e2e5: Read ./testprog 2 3

内容的提问来源于stack exchange,提问作者Sebastian Nuxoll

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 14:43:23