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.