(1)进程控制块:struct task_struct
Linux内核中每个进程都存在一个task_struct记录该进程信息,其中一个成员变量files 正是指向管理该进程所有打开文件信息的files_struct类型指针;
(2)文件管理器:struct files_struct
上面已经说了,struct files_struct结构体管理着进程打开的所有文件,该结构体核心成员如下图所示:
fd_array[64]为初始文件指针数组,存储文件指针*file,通过该数组便可索引该进程打开过的文件,但需注意该结构体只支持最大64个文件,超过64个文件会进行动态扩展。接下来看fdt与fdtab这两个成员,首先来了解struct fdtable这个结构体。
struct fdtable结构体为文件描述符表,其核心成员如下:
struct file **fd是一个指向file*的指针,通过fd可以访问fd_array[]存储的所有文件指针。max_fds表示fd_array[]储存的最大文件数。
回头来看struct files_struct中的fdtab成员与fdt成员,fdtab是内嵌的初始文件描述符表,fdtab.fd指针则指向了struct files_struct中的fd_array[64];而fdt成员又指向了fdtab。此时fdt->fd = fd_array,通过fdt->fd[i]即可访问索引为i的文件。
需要注意的是只有当打开文件数目少于64时,fdt才会指向fdtab。当打开文件数目超过64时,要进行动态扩展,其过程便是令fdt指向新创建的struct fdtable结构体,并将原fd_array[64]的数据拷贝到内存更大的新表。
(3)文件描述符:fd
fd是一个非负整数,本质上就是一个索引值,同上文fdt->fd[i]中的i。
(4)文件对象:struct file
前面已经提到fd 本质是数组索引,数组元素便是struct file结构体的指针,表征进程打开的文件。其简化结构如下图所示:

f_path:标识文件名;
f_inode:指向文件的inode对象,包含文件元数据(大小、权限、时间戳等);
f_op:文件操作函数表(虚拟函数表);
f_count:引用计数器,通过get_file()/fput()管理生命周期,当计数归零时调用f_op->release();
f_pos:当前文件读写位置,f_pos在open的时候会设置成默认值,seek的时候可以更改,从而影响到write/read的位置。
(5) 进程调用open打开文件过程
当进程调用open("/path/to/file",O_RDONLY)时经过以下几步:
解析路径:内核通过 目录项缓存(dcache)查找路径对应的dentry,进而找到inode。
创建struct file:分配一个新的file对象,关联到inode,并设置初始偏移量(f_pos=0)。
分配fd:在current->files->fdt->fd[]中找到一个空闲位置(如 fd=3),存储file*。
返回fd给用户态:用户程序后续通过fd操作文件。
(6) 进程调用read过程
通过fd找到file*:内核检查current->files->fdt->fd[fd],获取struct file*。
调用文件操作函数:file->f_op->read()。
检查页缓存:如果数据在缓存中(inode->i_mapping),直接拷贝到用户缓冲区。如果不在缓存,触发磁盘I/O,数据读入缓存后再返回。
更新文件偏移量:file->f_pos += 实际读取的字节数。

5101

被折叠的 条评论
为什么被折叠?



