На старій системі 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()вимагають параметра довжини до передачі.