TL; DR: родительский объект ДОЛЖЕН использовать небуферизованный ввод-вывод, если последующий процесс должен читать именно с того места, где родитель остановился.
При небуферизованном вводе-выводе программа ведет себя правильно:
#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'
$
Буферизованный ввод-вывод (через 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
(или аналогичный), мы можем наблюдать, сколько родительский процесс читает
s из стандартного ввода (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 байт (но получил только пять), а процесс exec
получил ноль, так как ничего не осталось. Поэтому не используйте буферизованное чтение в родительском процессе, если это проблема.