Linux 中的 file 对象:进程视角下的已打开文件
在 Linux 系统中,file 对象(文件对象)是进程与文件交互的核心接口,代表进程已打开的文件实例。它与 inode、dentry 共同构成了文件操作的底层框架,但 file 对象更贴近进程的操作视角,记录了进程访问文件的动态状态。
file 对象的本质与核心作用
什么是 file 对象?
file 对象是 Linux 内核在内存中创建的一种数据结构,由 open() 系统调用生成,随 close() 系统调用销毁。它仅存在于进程的上下文中,代表进程对某个文件的 “打开实例”,记录了该进程访问文件的具体方式和状态。
核心功能:
- 跟踪进程对文件的当前偏移量(读写位置);
- 记录文件的打开模式(如只读
O_RDONLY、读写O_RDWR、追加O_APPEND等); - 关联文件的底层元数据(通过指向 dentry 和 inode,间接关联文件的物理信息);
- 实现文件描述符(fd)与实际文件的映射(进程通过 fd 操作
file对象)。
file 对象与其他文件系统对象的关系
file 对象、dentry(目录项)、inode(索引节点)是 VFS(虚拟文件系统)的三大核心对象,三者的关系如下:
| 对象类型 | 本质 | 核心关联 | 唯一性特征 |
|---|---|---|---|
file |
进程打开的文件实例 | 通过 f_path.dentry 指向 dentry |
同一文件可被多个进程打开,对应多个 file 对象 |
| dentry | 文件名与 inode 的映射 | 通过 d_inode 指向 inode |
同一文件的硬链接对应多个 dentry,但指向同一 inode |
| inode | 文件的物理元数据 | 记录文件权限、数据块指针等 | 唯一标识一个文件(跨进程) |
形象比喻:
- inode 是文件的 “身份证”(唯一标识物理文件);
- dentry 是文件的 “户口本”(记录文件名及家庭关系);
file对象是进程的 “借阅证”(记录某进程如何使用该文件)。
file 对象的核心属性
file 对象包含多个关键成员,决定了进程与文件的交互方式,主要包括:
f_pos:文件当前偏移量(读写位置)- 记录进程下次读写文件的起始位置(以字节为单位);
- 例如,打开文件后默认
f_pos=0(从文件开头开始),执行read(200)后f_pos变为 200。
f_mode:文件打开模式- 包含进程对文件的访问权限(如
O_RDONLY只读、O_WRONLY只写、O_RDWR读写); - 以及特殊模式(如
O_APPEND每次写操作前自动将f_pos移至文件尾)。
- 包含进程对文件的访问权限(如
f_flags:文件状态标志- 如
O_NONBLOCK(非阻塞模式,避免 IO 操作阻塞进程)、O_SYNC(写操作同步到磁盘后才返回)等。
- 如
f_path:文件路径关联- 包含
dentry指针,通过f_path.dentry->d_inode可访问文件的 inode。
- 包含
f_op:文件操作函数集- 指向
file_operations结构体,包含读写(read/write)、seek(llseek)、关闭(release)等操作的函数指针; - 不同文件类型(如普通文件、设备文件、管道)的
f_op实现不同,体现了 VFS 的抽象能力。
- 指向
file 对象的生命周期与文件描述符
生命周期
- 创建:进程调用
open()或creat()时,内核检查文件权限后,创建file对象,关联 dentry 和 inode,并返回文件描述符(fd)。 - 使用:进程通过 fd 调用
read()、write()、lseek()等系统调用时,内核通过 fd 找到对应的file对象,执行相应操作(如更新f_pos)。 - 销毁:进程调用
close()或进程退出时,内核销毁file对象,若该file是最后一个引用 inode 的对象,则可能触发 inode 的回收(如硬链接数为 0 时)。
文件描述符(fd)的映射
进程通过文件描述符(一个非负整数,如 0、1、2 分别对应标准输入、输出、错误)操作 file 对象,其映射关系由进程的文件描述符表维护:
- 每个进程有独立的文件描述符表,表项指向内核中的
file对象; - 不同进程的相同 fd 可指向不同
file对象(如进程 A 的 fd=3 和进程 B 的 fd=3 可能对应不同文件); - 若多个进程打开同一文件,每个进程会获得独立的
file对象(f_pos等状态独立),但这些file对象指向同一 dentry 和 inode。
示例:
1 | // 进程 1 打开文件 |
file 对象的特性与常见场景
1. 同一文件的多个 file 对象
多个进程(或同一进程多次打开)同一文件时,会生成多个 file 对象,其特点:
- 共享 inode 和 dentry(因此文件内容、权限等元数据一致);
- 独立的
f_pos(读写位置互不影响,除非使用O_APPEND等特殊模式); - 独立的打开模式(如进程 A 只读打开,进程 B 可读写打开)。
示例:
- 进程 A 打开
test.txt后读取前 100 字节,f_pos=100; - 进程 B 同时打开
test.txt,默认f_pos=0,读取前 50 字节,f_pos=50; - 两者操作互不干扰,文件内容的修改会通过 inode 同步。
2. 父子进程的 file 对象继承
- 进程调用
fork()创建子进程时,子进程会复制父进程的文件描述符表,指向相同的file对象; - 此时父子进程的
file对象引用计数(f_count)加 1,共享f_pos等状态(对同一文件的读写会相互影响); - 任一进程调用
close()只会减少引用计数,直至计数为 0 时file对象才会被销毁。
3. 与文件锁的关系
file 对象是文件锁(如 flock() 或 fcntl() 锁)的载体:
- 内核通过
file对象的f_lock成员管理对该文件的锁定状态; - 同一
file对象上的锁会限制进程自身的操作,不同file对象上的锁(即使指向同一文件)会限制跨进程操作