В более старой системе RHEL, которая у меня есть, цикл не/bin/cat
выполняется . выдает сообщение об ошибке «cat: x: input file is output file». Я могу обмануть , делая это . Когда я пробую ваш код выше, я получаю "зацикливание", которое вы описываете. Я также написал системный вызов на основе «кота»: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;
}
Это петли тоже. Единственная буферизация здесь (в отличие от "mycat" на основе stdio) - это то, что происходит в ядре.
Я думаю, что происходит то, что файловый дескриптор 3 (результат open(av[1])
) имеет смещение в файл 0. Файловый дескриптор 1 (stdout) имеет смещение 3, потому что ">>" заставляет вызывающую оболочку делать lseek()
на дескриптор файла перед передачей его cat
дочернему процессу.
Выполнение read()
любого вида, будь то в stdio-буфере или обычном char buf[]
улучшении позиции файлового дескриптора 3. Выполнение write()
улучшении позиции файлового дескриптора 1. Эти два смещения - это разные числа. Из-за «>>» файловый дескриптор 1 всегда имеет смещение, большее или равное смещению файлового дескриптора 3. Таким образом, любая «подобная кошке» программа будет зацикливаться, если она не выполняет некоторую внутреннюю буферизацию. Возможно, возможно даже вероятно, что реализация stdio FILE *
(которая является типом символов stdout
и f
в вашем коде), которая включает в себя свой собственный буфер. fread()
может на самом деле сделать системный вызов read()
для заполнения внутреннего буфера fo f
.stdout
. Вызов fwrite()
наstdout
может или не может изменить что-либо внутриf
, Так что основанный на stdio «кот» может не зацикливаться. Или это может. Трудно сказать, не читая много уродливого, уродливого кода libc.
Я сделал strace
на RHEL cat
- он просто выполняет последовательность read()
и write()
системные вызовы. Но cat
не нужно работать таким образом. Это было бы возможно для mmap()
входного файла, а затем сделать write(1, mapped_address, input_file_size)
. Ядро сделало бы всю работу. Или вы можете сделать sendfile()
системный вызов между дескрипторами входного и выходного файлов в системах Linux. По слухам, старые системы SunOS 4.x делали трюк с отображением памяти, но я не знаю, делал ли кто-нибудь когда-нибудь кошку на основе sendfile. В любом случае , «зацикливание» бы не произошло, так как оба write()
и sendfile()
требуют параметра длины к передаче.