TLDR: o pai DEVE usar IO sem buffer se o processo subseqüente precisar ler exatamente de onde o pai parou.
Com E / S não armazenado em buffer, o programa se comporta corretamente:
#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);
}
Como executado via
$ make readtwo
cc readtwo.c -o readtwo
$ echo abcdefg | ./readtwo
./readtwo 'ab'
./childtu 'cd'
$
O IO com buffer (via fgets
) no pai é o problema, pois o filho só conseguirá ler a partir da entrada padrão se houver mais entrada do que o pai lê antecipadamente:
#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);
}
Se algum curioso puder procurar binário pelo tamanho exato do buffer, ou ver o que está configurado no kernel:
$ perl -e 'print(("a")x99999)' | ./readtwo
./readtwo 'aa'
./childtu 'aa'
$
Com strace
(ou similar), podemos observar quanto o processo pai read
s da entrada padrão (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
$
Aqui, o pai queria 4096 bytes (mas só tem cinco), e o processo exec
'd tem zero, pois não sobra nada. Portanto, não use uma leitura em buffer no processo pai se isso for um problema.