Em um sistema RHEL mais antigo eu tenho, /bin/cat não não faz loop para cat x >> x . cat fornece a mensagem de erro "cat: x: o arquivo de entrada é o arquivo de saída". Eu posso enganar /bin/cat fazendo isso: cat < x >> x . Quando eu tento seu código acima, recebo o "looping" que você descreve. Eu também escrevi uma chamada de sistema com base em "gato":
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int
main(int ac, char **av)
{
char buf[4906];
int fd, cc;
fd = open(av[1], O_RDONLY);
while ((cc = read(fd, buf, sizeof(buf))) > 0)
if (cc > 0) write(1, buf, cc);
close(fd);
return 0;
}
Isso faz um loop também. O único buffer aqui (diferentemente do "mycat" baseado em stdio) é o que acontece no kernel.
Acho que o que está acontecendo é que o descritor de arquivo 3 (o resultado de open(av[1]) ) tem um deslocamento no arquivo de 0. O descritor de arquivo 1 (stdout) tem um deslocamento de 3, porque o "> >" faz com que o shell de chamada faça um lseek() no descritor de arquivo antes de entregá-lo ao processo filho cat .
Fazer um read() de qualquer tipo, seja em um buffer stdio, ou um char buf[] simples avança a posição do descritor de arquivo 3. Fazer um write() avança a posição do descritor de arquivo 1. Esses dois deslocamentos são diferentes números. Por causa do "> >", o descritor de arquivo 1 sempre tem um deslocamento maior ou igual ao deslocamento do descritor de arquivo 3. Assim, qualquer programa "parecido com gato" fará um loop, a menos que faça algum buffer interno. É possível, talvez até provável, que uma implementação stdio de um FILE * (que é o tipo dos símbolos stdout e f em seu código) que inclui seu próprio buffer. fread() pode realmente fazer uma chamada de sistema read() para preencher o buffer interno para f . Isso pode ou não alterar qualquer coisa no interior de stdout . Chamar fwrite() on stdout pode ou não alterar qualquer coisa dentro de f . Portanto, um "gato" baseado em stdio pode não dar um loop. Ou talvez. Difícil dizer sem ler um monte de código feio e feio.
Eu fiz um strace no RHEL cat - ele faz apenas uma sucessão de chamadas de sistema read() e write() . Mas um cat não precisa funcionar dessa maneira. Seria possível mmap() do arquivo de entrada, em seguida, write(1, mapped_address, input_file_size) . O kernel faria todo o trabalho. Ou você poderia fazer uma chamada de sistema sendfile() entre os descritores de arquivo de entrada e saída nos sistemas Linux. Dizia-se que os antigos sistemas SunOS 4.x faziam o truque de mapeamento de memória, mas não sei se alguém já fez um gato baseado em sendfile. Em ambos os casos, o "looping" não aconteceria, já que write() e sendfile() exigem um parâmetro de comprimento para transferência.