以下を実行するプログラムAがあります。
- 入力から2バイトを読む
- 入力読み取りを印刷します。
exec
プログラムBを入力してください。
プログラムBは以下を行う。
- 入力から2バイトを読む
- 印刷入力
具体的には、オプションAとBです。
ㅏ:
#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'
$
(または同様の)方法を使用すると、親プロセスが標準入力(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プロセスは何も残っていなかったため、0を得ました。したがって、これが問題になる場合は、親プロセスでバッファリングされた読み取りを使用しないでください。