Trên một hệ thống RHEL cũ tôi đã có, /bin/cat
không không vòng lặp cho cat x >> x
. cat
đưa ra thông báo lỗi "cat: x: tệp đầu vào là tệp đầu ra". Tôi có thể đánh lừa /bin/cat
bằng cách này : cat < x >> x
. Khi tôi thử mã của bạn ở trên, tôi nhận được "vòng lặp" mà bạn mô tả. Tôi cũng đã viết một cuộc gọi hệ thống dựa trên "con mèo":
#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;
}
Vòng lặp này cũng vậy. Bộ đệm duy nhất ở đây (không giống như "mycat" dựa trên stdio) là những gì diễn ra trong kernel.
Tôi nghĩ những gì đang xảy ra là bộ mô tả tệp 3 (kết quả của open(av[1])
) có phần bù vào tệp 0. Bộ mô tả được ghi 1 (stdout) có độ lệch là 3, vì ">>" khiến trình vỏ gọi thực hiện lseek()
trên mô tả tập tin trước khi đưa nó cho cat
quá trình con.
Thực hiện read()
bất kỳ loại nào, cho dù vào bộ đệm stdio, hoặc đơn giản char buf[]
nâng cao vị trí của bộ mô tả tệp 3. Thực hiện write()
nâng cao vị trí của bộ mô tả tệp 1. Hai độ lệch này là các số khác nhau. Do ">>", bộ mô tả tệp 1 luôn có độ lệch lớn hơn hoặc bằng độ lệch của bộ mô tả tệp 3. Vì vậy, bất kỳ chương trình "giống như con mèo" nào cũng sẽ lặp lại, trừ khi nó thực hiện một số bộ đệm nội bộ. Có thể, thậm chí có khả năng, đó là một triển khai stdio của một FILE *
(đó là loại ký hiệu stdout
và f
trong mã của bạn) bao gồm bộ đệm của chính nó. fread()
thực sự có thể thực hiện một cuộc gọi hệ thống read()
để điền vào bộ đệm nội bộ cho f
. Điều này có thể hoặc không thể thay đổi bất cứ điều gì trong phần bên trong của stdout
. Gọi fwrite()
vàostdout
có thể hoặc không thể thay đổi bất cứ điều gì bên trong f
. Vì vậy, một "con mèo" dựa trên stdio có thể không lặp. Hoặc nó có thể. Khó có thể nói mà không đọc qua rất nhiều mã libc xấu xí, xấu xí.
Tôi đã thực hiện strace
trên RHEL cat
- nó chỉ thực hiện một loạt các cuộc gọi read()
và write()
hệ thống. Nhưng cat
không phải làm việc theo cách này. Nó sẽ có thể vào mmap()
các tập tin đầu vào, sau đó làm write(1, mapped_address, input_file_size)
. Nhân sẽ làm tất cả công việc. Hoặc bạn có thể thực hiện một sendfile()
cuộc gọi hệ thống giữa các mô tả tệp đầu vào và đầu ra trên các hệ thống Linux. Các hệ thống SunOS 4.x cũ đã được đồn đại để thực hiện thủ thuật lập bản đồ bộ nhớ, nhưng tôi không biết có ai đã từng làm một con mèo dựa trên sendfile chưa. Trong cả hai trường hợp, "vòng lặp" sẽ không xảy ra, vì cả hai write()
và sendfile()
yêu cầu tham số độ dài để chuyển.