Pada sistem RHEL tua saya punya, /bin/cat
tidak tidak loop untuk cat x >> x
. cat
memberikan pesan kesalahan "cat: x: file input adalah file output". Aku bisa menipu /bin/cat
dengan melakukan hal ini: cat < x >> x
. Ketika saya mencoba kode Anda di atas, saya mendapatkan "perulangan" yang Anda jelaskan. Saya juga menulis system call berbasis "kucing":
#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;
}
Loop ini juga. Satu-satunya buffering di sini (tidak seperti untuk "mycat" berbasis stdio) adalah apa yang terjadi di kernel.
Saya pikir apa yang terjadi adalah file deskriptor 3 (hasil open(av[1])
) memiliki offset ke file 0. Deskrip deskriptor 1 (stdout) memiliki offset 3, karena ">>" menyebabkan shell yang dipanggil untuk melakukanlseek()
pada deskriptor file sebelum menyerahkannya ke cat
proses anak.
Melakukan read()
apa pun, apakah menjadi buffer stdio, atau polos char buf[]
memajukan posisi deskriptor file 3. Melakukan write()
memajukan posisi deskriptor file 1. Kedua offset tersebut adalah angka yang berbeda. Karena ">>", deskriptor file 1 selalu memiliki offset lebih besar dari atau sama dengan offset deskriptor file 3. Jadi setiap program "seperti kucing" akan diulang, kecuali program tersebut melakukan buffering internal. Mungkin saja, bahkan mungkin, bahwa implementasi stdio dari FILE *
(yang merupakan jenis simbol stdout
dan f
dalam kode Anda) yang menyertakan buffernya sendiri. fread()
sebenarnya dapat melakukan panggilan sistem read()
untuk mengisi buffer internal untuk mungkin atau mungkin tidak mengubah apa pun di dalamnyaf
. Ini mungkin atau mungkin tidak mengubah apa pun di bagian dalam stdout
. memanggil fwrite()
distdout
f
. Jadi "kucing" berbasis stdio mungkin tidak berulang. Atau mungkin. Sulit dikatakan tanpa membaca banyak kode libc yang jelek dan jelek.
Saya melakukan strace
pada RHEL cat
- itu hanya melakukan suksesi read()
dan write()
panggilan sistem. Tetapi cat
tidak harus bekerja dengan cara ini. Itu mungkin untuk mmap()
file input, kemudian lakukan write(1, mapped_address, input_file_size)
. Kernel akan melakukan semua pekerjaan. Atau Anda bisa melakukan sendfile()
panggilan sistem antara deskriptor file input dan output pada sistem Linux. Sistem SunOS 4.x lama dikabarkan melakukan trik pemetaan memori, tapi saya tidak tahu apakah ada yang pernah menggunakan kucing berbasis sendfile. Dalam kedua kasus, "perulangan" tidak akan terjadi, karena keduanya write()
dan sendfile()
memerlukan parameter panjang-untuk-transfer.