Em um sistema RHEL mais antigo que eu tenho, não/bin/cat
faz loop para . dá a mensagem de erro "cat: x: arquivo de entrada é arquivo de saída". I pode enganar , fazendo isso: . Quando tento seu código acima, recebo o "loop" que você descreve. Também escrevi um "gato" baseado em chamada de sistema:cat x >> x
cat
/bin/cat
cat < x >> x
#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 dá laços também. O único buffer aqui (ao contrário 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 arquivado 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 cat
processo filho.
Executar read()
qualquer tipo, seja em um buffer stdio ou em uma planilha, char buf[]
avança a posição do descritor de arquivo 3. Executar a write()
avança a posição do descritor de arquivo 1. Esses dois deslocamentos são números diferentes. Por causa do ">>", o descritor de arquivo 1 sempre tem um deslocamento maior ou igual ao deslocamento do descritor de arquivo 3. Portanto, qualquer programa "semelhante a um gato" fará um loop, a menos que faça algum buffer interno. É possível, talvez até provável, que uma implementação stdio de a FILE *
(que é o tipo dos símbolos stdout
e f
no seu código) inclua seu próprio buffer. fread()
pode realmente fazer uma chamada do sistema read()
para preencher o buffer interno fo f
. Isso pode ou não mudar nada no interior de stdout
. chamando fwrite()
emstdout
pode ou não alterar nada dentro de f
. Portanto, um "gato" baseado em stdio pode não ser repetido. Ou pode. Difícil dizer sem ler muitos códigos libc feios e feios.
Eu fiz uma strace
no RHEL cat
- ele só faz uma sucessão de read()
e write()
chamadas do sistema. Mas a cat
não precisa funcionar dessa maneira. Seria possível para mmap()
o arquivo de entrada, então faça write(1, mapped_address, input_file_size)
. O kernel faria todo o trabalho. Ou você pode fazer uma sendfile()
chamada do sistema 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 arquivo de envio. Em ambos os casos, o "loop" não aconteceria, pois ambos write()
e sendfile()
requerem um parâmetro de comprimento para transferir.