【问题标题】:C system behavior on stdin with redirection带有重定向的标准输入上的 C 系统行为
【发布时间】:2015-06-19 04:51:44
【问题描述】:

我在玩了一下系统功能和文件编辑,发现了一个我不太了解的奇怪行为。

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

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

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

这是我的模拟示例:

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;
}

打印.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 程序时是否有一个技巧,说我希望“fork”有这个输入,而它的子进程有另一个输入。

【问题讨论】:

  • 问:你能给我们看看 fork() (以及,大概,随后的 exec())吗?
  • 分叉由系统完成(我的代码中为system("./print");)。
  • 来自man system,它像这样fork执行shell:execl("/bin/sh", "sh", "-c", command, (char *) 0);,所以它接收的io流完全不同。
  • 查看 system.c 和 man exec,execl 不会对文件描述符进行任何更改。看一下 strace,似乎父母一次消耗了全部输入,这可以解释为什么孩子无法阅读它。但我想知道是否有办法将某些东西传递给孩子(不改变 C 代码)。

标签: c fork system stdin file-descriptor


【解决方案1】:

完成乔纳森的回答。

孩子确实收到了EOF。原因是父母正在阅读的不仅仅是 fgets 请求。 Strace 显示父级读取了 4096 字节的块。 所以如果你想直接给子进程传递一些数据,你只需要在第 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

【讨论】:

    【解决方案2】:

    你应该在子进程中查看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;
    }
    

    样本运行

    在第一次运行中,我在键盘上输入了输入。打印 'similitu' 后,系统似乎挂起,所以我输入了 'miscellaneous',子进程报告了 '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
    $
    

    【讨论】:

      猜你喜欢
      • 2011-08-21
      • 2019-09-03
      • 1970-01-01
      • 2010-10-14
      • 1970-01-01
      • 2011-10-11
      • 1970-01-01
      • 2016-04-26
      • 1970-01-01
      相关资源
      最近更新 更多