1. 理解"文件"
1-1 狭义理解
- 文件在磁盘里
- 磁盘是永久性存储介质,因此文件在磁盘上的存储是永久性的
- 磁盘是外设(即是输出设备也是输入设备)
- 磁盘上的文件 本质是对文件的所有操作,都是对外设的输入和输出 简称 IO
1-2 广义理解
- Linux下⼀切皆文件(键盘、显示器、网卡、磁盘...... 这些都是抽象化的过程)
1-3 文件操作的归类认知
- 对于 0KB 的空文件是占用磁盘空间的
- 文件是文件属性(元数据)和文件内容的集合(文件 = 属性(元数据) + 内容)
- 所有的文件操作本质是文件内容操作和文件属性操作
1-4 系统角度
- 对文件的操作本质是进程对文件的操作
- 磁盘的管理者是操作系统
- 文件的读写本质不是通过C语言/C++的库函数来操作的(这些库函数只是为用户提供方便),而是通过文件相关的系统调用接口来实现的
2 自我梳理
一、文件的本质定义
文件 = 文件内容 + 文件属性(元数据)
- 后续所有文件操作,本质上就分为两类:
- 对文件内容的读写 / 修改
- 对文件属性的修改(如权限、大小、时间戳等)
二、文件访问的前提:路径与打开操作
-
访问文件必须先 "打开"
- 打开文件是程序运行过程中,由进程 通过
fopen动态完成的操作,不是静态行为。 - 进程是文件操作的主体,理解进程与文件的关系是核心。
- 打开文件是程序运行过程中,由进程 通过
-
文件路径的本质:绝对路径 vs 相对路径
- 访问文件必须依赖路径 + 文件名,没有 "无路径" 的访问方式。
- 代码中写
fopen("log.txt", "w");这种不带路径的写法,不是不需要路径,而是默认使用 ** 进程的当前工作目录(CWD,Current Working Directory)** 作为路径,最终等价于CWD/log.txt。 - 进程会动态维护自己的当前工作目录,可以通过
chdir系统调用修改。
三、打开文件的本质:从磁盘到内存
-
磁盘文件的特性
- 磁盘是永久性存储介质,文件在磁盘上是持久化存储的;同时磁盘是外设(兼具输入 / 输出功能),因此文件操作本质上是对磁盘的 IO 操作。
-
打开文件的核心行为
- 打开文件的过程,是将磁盘上的文件(包括内容和属性)加载到内存中。
- 后续所有对文件的读写操作,本质上都是进程通过 CPU 访问内存中的文件副本,再由操作系统同步到磁盘。
四、核心结论总结
- 文件操作的本质,是进程通过 IO 对磁盘文件的内容 和属性进行读写。
- 任何文件访问都依赖路径,相对路径的本质是使用进程的当前工作目录。
- 打开文件的过程,是将磁盘文件加载到内存,后续操作都是基于内存副本完成的。
3 文件操作

一、C 语言视角:文件操作与标准流
-
stdin/stdout/stderr是什么?- 它们是 C 语言标准库中预定义的
FILE*指针,分别对应:stdin:标准输入(键盘,文件描述符fd=0)stdout:标准输出(显示器,文件描述符fd=1)stderr:标准错误(显示器,文件描述符fd=2)
- 所有 C 语言文件操作函数(如
fputs、fwrite),最终都要通过FILE* stream参数指定操作的目标文件流。
- 它们是 C 语言标准库中预定义的
-
库函数 vs 系统调用
- 标准库函数(
fopen/fclose/fread/fwrite/fflush)是对 Linux 系统调用(open/close/read/write)的封装。 FILE结构体内部会持有文件描述符fd和缓冲区(如hellobit),实现带缓冲的 IO。
- 标准库函数(
二、内核视角:进程与打开文件的管理
-
文件描述符表(
fd_array)- 每个进程的
task_struct中,都有一个files_struct,它维护了一张文件描述符表fd_array。 - 表中的每个索引(如 0、1、2、3...)就是文件描述符
fd,指向内核中的struct file对象。 fd=0/1/2默认分别指向标准输入、输出、错误流,用户自己打开的文件会从fd=3开始分配。
- 每个进程的
-
struct file:内核文件对象- 内核为每个打开的文件创建一个
struct file实例,包含文件的状态信息(如文件位置指针、权限、引用计数等)。 - 不同进程打开同一个文件时,会各自创建独立的
struct file,但它们最终会指向同一个磁盘文件的inode(文件元数据)。
- 内核为每个打开的文件创建一个
三、文件操作的本质流程
-
从磁盘到内存:文件操作的前提
- 磁盘文件是持久化存储的,文件操作(读写)前必须先把文件加载到内存(内核缓冲区 / 用户缓冲区)。
- 磁盘文件 = 文件属性(元数据,存在 inode 中) + 文件内容(数据块)。
-
用户态 ↔ 内核态 ↔ 磁盘 的数据流向
- 用户调用 C 库函数(如
printf)→FILE缓冲区 → 系统调用(write)→ 内核缓冲区 → 最终刷写到磁盘。 - 直接使用系统调用(
write)没有用户态缓冲区,而 C 库函数带缓冲,效率更高。
- 用户调用 C 库函数(如
四、关键问题与补充知识点
-
为什么 C 语言要封装系统调用?
- 跨平台性 :不同操作系统的系统调用接口不兼容(如 Linux 的
write和 Windows 的WriteFile),C 标准库屏蔽了底层差异,实现 "一次编写,到处运行"。 - 缓冲优化:用户态缓冲减少了系统调用次数,大幅提升 IO 效率。
- 跨平台性 :不同操作系统的系统调用接口不兼容(如 Linux 的
-
FILE结构体与fd的关系FILE是 C 库提供的用户态句柄,它的成员中包含了内核的文件描述符fd,以及用户态缓冲区、标志位等信息。- 调用
fopen时,C 库会先调用open系统调用拿到fd,再初始化一个FILE对象;fclose则会刷新缓冲区并调用close系统调用。
-
C++ 文件流的补充
- C++ 的
ifstream/ofstream也是对底层文件操作的封装,和 C 库的FILE类似,同样基于文件描述符和缓冲机制。
- C++ 的
五、核心结论总结
- 用户态的文件操作(C 库 / C++ 流),本质上都是通过文件描述符,间接操作内核中的文件对象,最终访问磁盘文件。
- 文件描述符
fd是进程与内核文件对象的桥梁,FILE是 C 库为了实现跨平台和缓冲优化,在用户态封装的一层。 - 打开文件的本质,是内核为进程创建文件对象、分配文件描述符,并建立进程与磁盘文件的关联。