多进程下重定向文件到stdin使用fgets读取行的异常问题
问题分析
你的问题核心在于fork后所有进程共享同一个stdin的文件偏移量和stdio缓冲区,具体原因如下:
- 当stdin重定向到文件时,C标准库默认使用全缓冲(手动输入时是行缓冲),fork后所有子进程会继承父进程已经填充的stdio缓冲区。
- 父进程执行
fgets读取第一行后,文件偏移量已经移动到下一行,同时stdio缓冲区中可能已经缓存了文件后续内容。子进程继承这个缓冲区和偏移量,再次调用fgets时会读取缓冲区中的残留数据,或者因为偏移量混乱导致读取到错误内容(也就是你看到的乱码)。 - 当前代码中每个进程只读取一次就退出,且没有任何同步机制,多个进程同时读取会互相干扰。
解决方案
下面是修正后的代码,解决了上述问题:
#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
相关产品推荐
相关产品推荐

