На старій системі RHEL я отримав, /bin/cat
робить НЕ петля для cat x >> x
. cat
видає повідомлення про помилку "cat: x: вхідний файл - вихідний файл". Я можу обдурити /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()
вимагають параметра довжини до передачі.