Stdin

執行的程序未從標準輸入讀取

  • April 4, 2017

我有一個程序 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

但我明白了

為什麼不B從其標準輸入讀取?

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'
$ 

使用(或類似的)我們可以從標準輸入(fd 0)strace中觀察到父程序有多少:read

$ 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 程序得到了零,因為什麼都沒有了。因此,如果這是一個問題,請不要在父程序中使用緩衝讀取。

引用自:https://unix.stackexchange.com/questions/355904