带重定向的 stdin 上的 C 系统行为

C system behavior on stdin with redirection

我正在研究系统功能和文件编辑,发现一个我不太理解的奇怪行为。

我有一个读取 stdin 的第一个进程,然后在另一个也读取 stdin 的进程上分叉(调用系统函数)。

在第一种情况下,我没有将任何内容重定向到第一个进程的标准输入,一切都按预期工作,两个进程都从我的提示中读取。

但在第二种情况下,我将文件通过管道或重定向到第一个进程,分叉进程开始读取 "random" 数据。

这是我的模拟示例:

fork.c

#include <stdlib.h>
#include <stdio.h>

int main() {
    char buf[10];
    fgets(buf,10,stdin);
    printf("Buff: %s\n", buf);
    system("./print");
    fgets(buf,10,stdin);
    printf("Buff: %s\n", buf);
    return 0;
}

print.c

#include <stdlib.h>
#include <stdio.h>

int main() {
    char buf[10];
    fgets(buf,10,stdin);
    printf("Buff: %s\n", buf);
}

如果我运行

echo "just another awesome input" | ./fork

我明白了

Buff: just anot
Buff: Ðhuÿ
Buff: her aweso

而对于 child,输入总是不同的(我猜它正在读取内存中的某个随机位置...)。

谁能解释一下这种行为?我知道 child 继承了 fork 的文件描述符,但我不明白为什么它不能只读取与 parent 相同的输入,或者至少是确定性的。

我想知道在调用我的 fork 程序时是否有技巧说我希望 "fork" 有这个输入,它 child 另一个输入。

你应该看看child进程中fgets()返回的错误状态,在fgets()报错的时候报EOF

parent 进程已从管道中读取了一个充满信息的缓冲区,并将以 9 个字符 + 空字节增量将其分发出去。由于信息已经从管道中消失,child 无法读取任何内容并报告 EOF。 parent 从其缓冲输入中继续 material。

始终 检查 I/O 调用(尤其是输入调用)是否成功。

fork.c

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    char buf[10];
    if (fgets(buf,10,stdin) != 0)
      printf("Buff-1: %s\n", buf);
    else
      printf("Buff-1: EOF detected\n");
    system("./print");
    if (fgets(buf,10,stdin) != 0)
      printf("Buff-2: %s\n", buf);
    else
      printf("Buff-2: EOF detected\n");
    return 0;
}

print.c

#include <stdio.h>

int main(void)
{
    char buf[10];
    if (fgets(buf,10,stdin) != 0)
      printf("Buff-C: %s\n", buf);
    else
      printf("Buff-C: <<EOF>>\n");
    return 0;
}

样本运行s

在第一个运行中,我在键盘上输入了输入。打印 'similitud' 后,系统出现挂起,所以我输入 'miscellaneous',child 进程报告 'miscellan' (如果继续,可能会报告 'eous'读)。与此同时,parent 进程继续报告来自 'ude' 行的数据。

在第二个示例中,正如预测的那样,child 进程报告了 EOF,因为 parent 在其第一个输入操作中从管道读取了所有数据。

$ ./fork
similitude and gratitude and attitude and longitude, dude!
Buff-1: similitud
miscellaneous
Buff-C: miscellan
Buff-2: e and gra
$ echo "just another awesome input" | ./fork
Buff-1: just anot
Buff-C: <<EOF>>
Buff-2: her aweso
$

完成 Jonathan 的回答。

child确实收到了EOF。原因是 parent 读取的不仅仅是 fgets 请求。 Strace 显示 parent 读取 4096 字节的块。 所以如果你想将一些数据直接传递给child进程,你只需要在4096个第一个字节之后写。

例如

$ python -c 'print "0*4096"+"123"' | ./fork
Buff: 0000000000
Buff: 123

Buff: 0000000000

另一个有趣的方法是使用 cat 从提示中读取。

$ (echo "once upon a time";cat) | ./fork
Buff: once upon
test               <-- I am prompted here
Buff: test

Buff:  a time