引言
在 C 语言文件操作的学习中,我们常遇到两个核心问题:为什么说操作系统中'一切皆文件'?printf、fwrite这些库函数比系统调用 write更高效的秘密是什么?这两个问题的答案,其实都指向同一个关键概念——缓冲区。
很多开发者对文件操作的认知停留在'调用函数读写数据'的表层,却忽略了缓冲区的存在。它是 libc 库为提升性能设计的中间层,也是连接用户代码与系统内核的重要桥梁。而'一切皆文件'的理念,则为键盘、显示器、磁盘文件等不同设备提供了统一的操作接口,让缓冲区的复用成为可能。
本文将从底层逻辑切入,逐步拆解缓冲区的本质、引入原因与三种缓冲类型,再通过实际现象观察验证缓冲机制的存在。最终,我们会亲手设计一个简化版的 libc 文件操作库,让你从使用者转变为设计者,彻底理解 C 语言文件操作的底层逻辑。
理解'一切皆文件'
在 Linux 中,不仅是普通文件,进程、磁盘、显示器、键盘等硬件设备也被抽象成了文件。你可以使用访问文件的方法访问它们获得信息;甚至管道和 Socket(套接字),使用的接口跟文件接口也是一致的。这样做最明显的好处是,开发者仅需要使用一套 API 和开发工具,即可调取 Linux 系统中绝大部分的资源。
例如,Linux 中几乎所有读操作都可以用 read 函数来进行;几乎所有更改操作都可以用 write 函数来进行。
之前我们讲过,当打开一个文件时,操作系统为了管理所打开的文件,都会为这个文件创建一个 file结构体。
struct file { ... struct inode *f_inode; /* cached value */ const struct file_operations *f_op; ... atomic_long_t f_count; // 表⽰打开⽂件的引⽤计数 unsigned int f_flags; // 表⽰打开⽂件的权限 fmode_t f_mode; // 设置对⽂件的访问模式 loff_t f_pos; // 表⽰当前读写⽂件的位置 ... } __attribute__((aligned(4)));
值得关注的是 struct file中的 f_op指针指向了一个 file_operations 结构体,这个结构体中的成员除了 struct module* owner 其余都是函数指针。
struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, *); (*write) ( file *, __user *, , *); (*open) ( inode *, file *); (*release) ( inode *, file *);



