执行的进程不从标准输入读取

执行的进程不从标准输入读取

我有一个程序 A 执行以下操作:

  1. 从输入读取 2 个字节
  2. 打印读取的输入
  3. exec进入程序B。

程序B执行以下操作

  1. 从输入读取 2 个字节
  2. 打印输入

具体来说,以下是方案A、B:

A:

#include <unistd.h>
#include <stdio.h>

int main(){
  char a[3];
  fgets(a, 3, stdin);
  printf("%s\n", a);
  char* args[] = {NULL};
  execv("./read2", args);
}

乙:

#include <stdio.h>

int main(){
   char a[] = "hy";
   fgets(a, 3, stdin);
   printf("%s\n", a);
}

当我像这样执行它时echo 'abcd' | ./A,我期望得到以下输出

ab
cd

但我得到了 ab hy

为什么不B从其标准输入读取?

答案1

TLDR:如果后续进程需要从父进程停止的位置准确读取,则父进程必须使用无缓冲 IO。

对于非缓冲 IO,程序的行为正确:

#include <stdio.h>
#include <string.h>
#include <unistd.h>

char buf[3];

int main(int argc, char *argv[])
{
    read(STDIN_FILENO, buf, 2);
    printf("%s '%s'\n", *argv, buf);
    if (strcmp(*argv, "./childtu") == 0) return 0;
    execl("./readtwo", "./childtu", (char *) 0);
}

通过运行

$ make readtwo            
cc     readtwo.c   -o readtwo
$ echo abcdefg | ./readtwo
./readtwo 'ab'
./childtu 'cd'
$ 

父级中的缓冲 IO(通过fgets)是问题所在,因为如果输入比父级提前读取的输入多,则子级只能从标准输入读取:

#include <stdio.h>
#include <string.h>
#include <unistd.h>

char buf[3];

int main(int argc, char *argv[])
{
    fgets(buf, 3, stdin);
    printf("%s '%s'\n", *argv, buf);
    if (strcmp(*argv, "./childtu") == 0) return 0;
    execl("./readtwo", "./childtu", (char *) 0);
}

如果好奇,可以二进制搜索确切的缓冲区大小,或者查看内核中设置的内容:

$ perl -e 'print(("a")x99999)' | ./readtwo
./readtwo 'aa'
./childtu 'aa'
$ 

使用strace(或类似的)我们可以观察父进程有多少read来自标准输入(fd 0):

$ echo asdf | strace -o blah ./readtwo
./readtwo 'as'
./childtu ''
$ fgrep 'read(0' blah
read(0, "asdf\n", 4096)                 = 5
read(0, "", 4096)                       = 0
$

在这里,父进程想要​​ 4096 字节(但只得到了 5 个字节),而exec'd 进程得到了零,因为没有剩下任何东西。因此,如果这是一个问题,请不要在父进程中使用缓冲读取。

相关内容