Su un vecchio sistema di RHEL che ho, /bin/cat
lo fa senza anello per cat x >> x
. cat
visualizza il messaggio di errore "cat: x: il file di input è un file di output". Posso ingannare /bin/cat
in questo modo: cat < x >> x
. Quando provo il tuo codice sopra, ottengo il "looping" che descrivi. Ho anche scritto un "gatto" basato sulla chiamata di sistema:
#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;
}
Anche questo loop. L'unico buffering qui (a differenza di "mycat" basato su stdio) è ciò che accade nel kernel.
Penso che quello che sta accadendo è che il file descrittore 3 (il risultato di open(av[1])
) ha un offset nel file di 0. Archiviato descrittore 1 (stdout) ha un offset di 3, perché la ">>" fa sì che la shell invocazione per fare un lseek()
in descrittore di file prima di consegnarlo al cat
processo figlio.
Fare un read()
qualsiasi tipo, sia in un buffer stdio, sia in una semplice, fa char buf[]
avanzare la posizione del descrittore di file 3. Fare una write()
posizione fa avanzare la posizione del descrittore di file 1. Questi due offset sono numeri diversi. A causa del ">>", il descrittore di file 1 ha sempre un offset maggiore o uguale all'offset del descrittore di file 3. Quindi qualsiasi programma "simile a un gatto" eseguirà il ciclo, a meno che non esegua un buffering interno. È possibile, forse anche probabile, che un'implementazione stdio di un FILE *
(che è il tipo di simboli stdout
e f
nel tuo codice) che include il proprio buffer. fread()
può effettivamente effettuare una chiamata di sistema read()
per riempire il buffer interno fo f
. Questo può o non può cambiare nulla all'interno stdout
. Chiamata fwrite()
sustdout
può o meno cambiare qualcosa all'interno di f
. Quindi un "gatto" basato su stdio potrebbe non essere in loop. O potrebbe. Difficile dirlo senza leggere un sacco di brutti e brutti codici libc.
Ho fatto un strace
RHEL cat
- fa solo una serie di read()
e write()
chiamate di sistema. Ma a cat
non deve funzionare in questo modo. Sarebbe possibile al mmap()
file di input, quindi fare write(1, mapped_address, input_file_size)
. Il kernel farebbe tutto il lavoro. Oppure potresti fare una sendfile()
chiamata di sistema tra i descrittori dei file di input e output sui sistemi Linux. Si diceva che i vecchi sistemi SunOS 4.x facessero il trucco della mappatura della memoria, ma non so se qualcuno abbia mai fatto un gatto basato su sendfile. In entrambi i casi il "looping" non si verificherebbe, in quanto entrambi write()
e sendfile()
richiedono un parametro di lunghezza da trasferire.