我有这个小程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
int orig = 1;
for (int i = 0; (i != 3) && orig; ++i) {
orig = orig && fork();
}
if (orig) {
for (int i = 0; i != 3; ++i) {
wait(NULL);
}
} else {
int num;
scanf("%8d", &num);
printf("%d\n", num*num);
}
}
它应该简单地计算从 stdin
读取的三个数字的平方。如果我自己输入数字,它就会起作用:
akiiino$ ./out.out
12345678
12345678
12345678
260846532
260846532
260846532
但是如果我将 cat 用于相同的目的,它不会按预期工作:
akiiino$ cat in.txt
12345678
12345678
12345678
akiiino$ cat in.txt | ./out.out
260846532
0
0
这种奇怪行为背后的原因是什么?可以修复吗?我一直认为cat
文件与将它们输入stdin
没有什么不同。
最佳答案
不同之处在于,当您手动输入 3 个数字时,您正在从终端读取数据,并且低级读取会在换行符处终止。让我们看看幕后发生了什么。
手动输入:
- 3 个 child 已经开始并正在等待输入
- 您输入一个数字和一个新行
- 底层
read
调用由换行符终止,因为您从终端读取 - 第一个 child (获得读取的 child )的
scanf
调用对输入进行解码并进行处理
好的,我们对另外 2 个 child 进行同样的操作
从文件或管道中读取
- 3 个 child 已经开始并正在等待输入
- 这 3 个数字和换行符立即可用
- 第一个子进程的底层
read
读取并消耗stdio缓冲区中的所有输入 - 第一个 child (获得读取的 child )的
scanf
调用对输入(第一部分)进行解码并进行处理
但是现在另外 2 个 child 正在从位于文件末尾的文件/管道中读取。他们的 scanf 返回 0 但你无法控制它,他们让
num
中初始未确定的值。
您可以通过在循环之前添加 sleep(10)
来控制这不是竞争条件问题,在第一个子级开始读取之前启动程序并手动输入 3 个数字。由于低级读取将以换行符终止(特殊的 tty 情况),即使 3 行在第一次读取之前可用,您仍然会得到与第一种情况相同的输出。
关于cat 会破坏程序,手动 stdin 输入不会,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43278397/