Sur un ancien système RHEL que j'ai, /bin/cat
ne fait pas de boucle cat x >> x
. cat
donne le message d'erreur "cat: x: le fichier d'entrée est le fichier de sortie". Je peux tromper /bin/cat
en faisant ceci: cat < x >> x
. Lorsque j'essaie votre code ci-dessus, j'obtiens le "bouclage" que vous décrivez. J'ai également écrit un "chat" basé sur les appels système:
#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;
}
Cela boucle aussi. La seule mise en mémoire tampon ici (contrairement à "mycat" basé sur stdio) est ce qui se passe dans le noyau.
Je pense que ce qui se passe est que le descripteur de fichier 3 (le résultat de open(av[1])
) a un décalage dans le fichier de 0. Le descripteur de fichier 1 (stdout) a un décalage de 3, car le ">>" fait que le shell appelant fait un lseek()
sur le descripteur de fichier avant de le transmettre au cat
processus enfant.
Faire un read()
de n'importe quelle sorte, que ce soit dans un tampon stdio ou un simple, char buf[]
avance la position du descripteur de fichier 3. Faire un write()
avance la position du descripteur de fichier 1. Ces deux décalages sont des nombres différents. En raison du ">>", le descripteur de fichier 1 a toujours un décalage supérieur ou égal à l'offset du descripteur de fichier 3. Ainsi, tout programme "semblable à un chat" bouclera, à moins qu'il ne fasse un tampon interne. Il est possible, voire probable, qu'une implémentation stdio d'un FILE *
(qui est le type des symboles stdout
et f
dans votre code) qui inclut son propre tampon. fread()
peut en fait faire un appel système read()
pour remplir le tampon interne fo f
. Cela peut ou ne peut rien changer à l'intérieur de stdout
. Appel fwrite()
surstdout
peut ou ne peut rien changer à l'intérieur de f
. Un "chat" basé sur stdio peut donc ne pas boucler. Ou peut-être. Difficile à dire sans lire beaucoup de code libc laid et laid.
Je l' ai fait un strace
sur la RHEL cat
- il fait juste une succession de read()
et write()
appels système. Mais un cat
ne doit pas fonctionner de cette façon. Il serait possible pour mmap()
le fichier d'entrée, alors faites write(1, mapped_address, input_file_size)
. Le noyau ferait tout le travail. Ou vous pouvez faire un sendfile()
appel système entre les descripteurs de fichiers d'entrée et de sortie sur les systèmes Linux. Les vieux systèmes SunOS 4.x étaient censés faire l'affaire de mappage de la mémoire, mais je ne sais pas si quelqu'un a déjà fait un chat basé sur sendfile. Dans les deux cas, le "bouclage" ne se produirait pas, car les deux write()
et sendfile()
nécessitent un paramètre de longueur à transférer.