En un sistema más antiguo RHEL lo que tengo, /bin/cat
lo hace no lazo para cat x >> x
. cat
da el mensaje de error "cat: x: el archivo de entrada es el archivo de salida". Puedo engañar /bin/cat
al hacer esto: cat < x >> x
. Cuando pruebo su código anterior, obtengo el "bucle" que describe. También escribí una llamada al sistema basada en "cat":
#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;
}
Esto también se repite. El único búfer aquí (a diferencia de "mycat" basado en stdio) es lo que sucede en el núcleo.
Creo que lo que está sucediendo es que el descriptor de archivo 3 (el resultado de open(av[1])
) tiene un desplazamiento dentro del fichero de 0. 1 Filed descriptor (stdout) cuenta con un desplazamiento de 3, debido a que el ">>" hace que el shell que invoca a hacer una lseek()
en el descriptor de archivo antes de entregarlo al cat
proceso secundario.
Hacer una read()
de cualquier tipo, ya sea en un búfer stdio o en un plano, char buf[]
avanza la posición del descriptor de archivo 3. Hacer un write()
avance de la posición del descriptor de archivo 1. Esos dos desplazamientos son números diferentes. Debido al ">>", el descriptor de archivo 1 siempre tiene un desplazamiento mayor o igual que el desplazamiento del descriptor de archivo 3. Por lo tanto, cualquier programa "similar a un gato" se repetirá, a menos que haga un búfer interno. Es posible, incluso probable, que una implementación estándar de un FILE *
(que es el tipo de símbolos stdout
y f
en su código) que incluya su propio búfer. fread()
en realidad puede hacer una llamada read()
al sistema para llenar el búfer interno fo f
. Esto puede o no cambiar nada en el interior de stdout
. llamando fwrite()
enstdout
puede o no cambiar nada en el interior de f
. Por lo tanto, un "gato" basado en stdio podría no repetirse. O que podría hacerlo. Difícil de decir sin leer un montón de código libc feo y feo.
Hice una strace
en la RHEL cat
- sólo se hace una sucesión de read()
y write()
llamadas al sistema. Pero a cat
no tiene que funcionar de esta manera. Sería posible mmap()
ingresar el archivo, luego hacer write(1, mapped_address, input_file_size)
. El núcleo haría todo el trabajo. O puede hacer una sendfile()
llamada al sistema entre los descriptores de los archivos de entrada y salida en los sistemas Linux. Se rumoreaba que los viejos sistemas SunOS 4.x hacían el truco de mapeo de memoria, pero no sé si alguien había hecho un gato basado en sendfile. En cualquier caso, el "bucle" sería no ocurrir, ya que tanto write()
y sendfile()
requerir un parámetro de longitud a transferencia.