具有重定向的stdin上的C系统行为

时间:2015-04-13 18:45:36

标签: c fork system stdin file-descriptor

我正在玩系统功能和文件重写,发现了一个我不太懂的奇怪行为。

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

在第一种情况下,我不会将任何内容重定向到第一个流程' stdin,一切都按预期工作,两个进程都从我的提示中读取。

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

以下是我的模拟示例:

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

对于孩子来说,输入总是不同的(我猜它是在内存中读取一些随机位置......)。

任何人都可以解释这种行为吗?我知道孩子在fork处继承了文件描述符,但我不明白为什么它只能读取与父级相同的输入,或者至少是确定性的。

我想知道在调用我的fork程序时是否有诀窍说我想要&#34; fork&#34;有这个输入,它的孩子是另一个输入。

2 个答案:

答案 0 :(得分:1)

您应该查看子进程中fgets()返回的错误状态,并在fgets()报告时报告EOF。

父进程从管道中读取了一个充满信息的缓冲区,并以9个字符+空字节为增量进行处理。由于信息已经从管道消失,孩子无法读取任何内容并报告EOF。父级继续使用缓冲输入中的材料。

始终检查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;
}

样品运行

在第一次运行中,我在键盘上输入了输入。打印'similitud'后,系统似乎挂起,所以我输入'杂项',并且子进程报告'miscellan'(并且可能已经报告'eous'继续阅读)。与此同时,父流程继续报告来自'ude'行的数据。

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

$ ./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
$

答案 1 :(得分:0)

完成乔纳森的回答。

孩子确实收到了EOF。原因是父母正在阅读的不只是fgets请求。 Strace显示父级读取4096字节的块。 因此,如果您想将一些数据直接传递给子进程,您只需要在4096个第一个字节之后写入。

E.g。

$ 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