我在编写C程序打印文件的行时遇到了一个问题。假设该文件有5行,它应该创建5个子进程。每个子进程从文件中读取一行并打印相应的行。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define BUFSIZE 256
#define ITR 1e6
int main(int argc, char *argv[]) {
if (argc != 2) exit(1);
pid_t pid;
char buffer[BUFSIZE] = { 0 }; // create buffer
FILE *fp = fopen(argv[1], "r"); // read file into *fp
while (fgets(buffer, BUFSIZE, fp)) {
pid = fork();
if (pid == 0) {
for (int j = 0; j < ITR; j++);
printf("%sn", buffer);
_exit(0); // exit child
}
}
wait(NULL); // wait for child to finish
return 0;
}
原文件为
Child 1 reads this linen
Child 3 reads this linen
Child 2 reads this linen
Child 4 reads this linen
输出如下所示:
Child 1 reads this linen
n
Child 2 reads this linen
n
Child 3 reads this linen
n
Child 4 reads this linen
n
但是我不希望在两行之间有额外的n
。因此,我将printf("%sn", buffer);
修改为printf("%s", buffer);
,这样就只有原始文件中的1个n
了。但是输出变成
Child 1 reads this line
Child 2 reads this line
Child 3 reads this line
最后一行应该是Child 4 reads this line
,但没有像我预期的那样打印出来。有人知道为什么会这样吗?
这里有一些你应该调查的问题:
-
您应该使用
exit(0)
或return 0
而不是_exit(0)
:非标准函数_exit()
,相当于linux上的_Exit()
,可能无法正确刷新标准流缓冲区:7.22.4.5 _Exit函数
包含未写入缓冲数据的打开流是否被刷新、关闭或删除临时文件是由实现定义的。 -
循环
for (int j = 0; j < ITR; j++);
没有副作用,所以它很可能被编译器优化掉了。 -
stdout
到终端默认是行缓冲的,所以printf("%sn", buffer)
发出2个单独的系统调用来刷新输出到系统:- 一个用于以换行符和 结尾的缓冲区内容
- 打开
n
格式字符串。
这些系统调用可能在不同的子进程之间交错。您应该在第一个输出操作之前使用
setvbuf(stdout, NULL, BUFSIZ, _IOFBF)
更改缓冲方法,在本例中,在printf
足够之前。
这是一个修改后的版本,你可以试试:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define BUFSIZE 256
#define ITR 1e6
int main(int argc, char *argv[]) {
if (argc != 2) exit(1);
pid_t pid;
char buffer[BUFSIZE] = { 0 }; // create buffer
FILE *fp = fopen(argv[1], "r"); // read file into *fp
int lineno = 1;
while (fp && fgets(buffer, BUFSIZE, fp)) {
pid = fork();
if (pid == 0) {
setvbuf(stdout, NULL, BUFSIZ, _IOFBF);
printf("%d: %sn", lineno, buffer);
exit(0); // exit child
}
lineno++;
}
wait(NULL); // wait for child to finish
return 0;
}
在我的系统中,每行输出都有一个额外的换行符,但不一定与输入文件的顺序相同。