《APUE》里文件 IO 的「文件表项」和内核 VFS 的 struct file 关系?

曾经看《APUE》,说内核使用三种数据结构表示打开的文件,分别是文件描述符表、文件表和 V 节点表,如下图是两个进程打开同一个文件的内核数据结构。 后来看了Linux VFS的四大结构(superblock\dentry\inode\file),又想起APUE里这三个结构,我的理解是v 节点表项是VFS的inode结构,而文件表就是VFS里的struct file结构。对吗?
关注者
39
被浏览
1551

4 个回答

谢邀~
linux的进程是由PCB管理,也就是task_struct结构,而task_struct结构中有一个指针指向files_struct结构,其描述如下
struct files_struct {
  /* count为文件表files_struct的引用计数 */
    atomic_t count;
    /* 文件描述符表 */
    /*
     为什么有两个fdtable呢?这是内核的一种优化策略。fdt为指针, 而fdtab为普通变量。一般情况下, 
     fdt是指向fdtab的, 当需要它的时候, 才会真正动态申请内存。因为默认大小的文件表足以应付大多数
     情况, 因此这样就可以避免频繁的内存申请。
     这也是内核的常用技巧之一。在创建时, 使用普通的变量或者数组, 然后让指针指向它, 作为默认情况使
     用。只有当进程使用量超过默认值时, 才会动态申请内存。
    *//*
    struct fdtable __rcu *fdt;
    struct fdtable fdtab;
    * written part on a separate cache line in SMP
    */
    /* 使用____cacheline_aligned_in_smp可以保证file_lock是以cache
     line 对齐的, 避免了false sharing */
    spinlock_t file_lock ____cacheline_aligned_in_smp;
    /* 用于查找下一个空闲的fd */
    int next_fd;
    /* 保存执行exec需要关闭的文件描述符的位图 */
    struct embedded_fd_set close_on_exec_init;
    /* 保存打开的文件描述符的位图 */
    struct embedded_fd_set open_fds_init;
    /* fd_array为一个固定大小的file结构数组。struct file是内核用于文
    件管理的结构。这里使用默认大小的数组, 就是为了可以涵盖大多数情况, 避免动
    态分配 */
    struct file __rcu * fd_array[NR_OPEN_DEFAULT];
}; 
这里要注意2个结构,一个是fdtable结构指针(可以看做是文件描述符表),另外一个是struct file *fd_array结构(file对象数组,其元素file类型也就是题主图的文件表(这里翻译不准确))
struct fdtable {
 unsigned int max_fds;               //可以代开的最大文件数
 int max_fdset;                      //位图的最大长度
 int next_fd;                        //下一个可用的fd
 struct file ** fd;      /* current fd array 指向files_struct的fd_array */
 fd_set *close_on_exec;
 fd_set *open_fds;                   //打开的文件标记,比如第2位为0,则打开了2号文件
 struct rcu_head rcu;
 struct files_struct *free_files;
 struct fdtable *next;
};
struct fdtable结构中的二级指针指向一个file对象数组,而file对象保存的就是偏移量,引用计数等文件信息
struct file 
{
 struct list_head        f_list;    /*所有打开的文件形成一个链表*/
 struct dentry           *f_dentry; /*指向相关目录项的指针*/
 struct vfsmount         *f_vfsmnt; /*指向VFS安装点的指针*/
 struct file_operations  *f_op;     /*指向文件操作表的指针*/
 mode_t f_mode;                                  /*文件的打开模式*/
 loff_t f_pos;                                   /*文件的当前位置*/
 unsigned short f_flags;                         /*打开文件时所指定的标志*/
 unsigned short f_count;                           /*使用该结构的进程数*/
 unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;
 /*预读标志、要预读的最多页面数、上次预读后的文件指针、预读的字节数以及预读的页面数*/
 int f_owner;                  /* 通过信号进行异步I/O数据的传送*/
 unsigned int         f_uid, f_gid;  /*用户的UID和GID*/
 int                 f_error;       /*网络写操作的错误码*/
 
 unsigned long f_version;           /*版本号*/
 void *private_data;                      /* tty驱动程序所需 */
};

从file的结构看来其中包含一个指针,指向dentry对象。dentry对象代表一个独立的文件路径,如果一个文件路径被打开多次,那么会建立多个file对象,但它们都指向同一个dentry对象。
dentry对象中又包含一个指向inode对象的指针。inode对象代表一个独立文件。因为存在硬链接与符号链接,因此不同的dentry 对象可以指向相同的inode对象.inode 对象包含了最终对文件进行操作所需的所有信息,如文件大小、文件的操作方法、文件的权限、访问日期等。
打开文件后,进程得到的文件描述符实质上就是文件描述符表fdtable的下标,内核根据这个下标值去访问相应的文件对象file,从而实现对文件的操作。
来一张比较完整的图
没啥关系吧,这个说的是内核数据,跟文件系统关系不大。
文件指针跟文件表就是指针跟结构的关系,每打开一个文件,都会有这个信息。这个是跟文件系统无关的,任何文件系统,包括设备都是这样子的。v节点表才是表示物理文件信息的,看不同的文件系统,可以是一个v节点对应一个物理文件,也可以是v节点链表对应一个文件,这里指向的v节点只是链表头。vfs是管理文件系统的,vfs侧重的是内核设计实现问题,而你这里说的是更侧重api接口问题。你看下c语言的文件接口,就会找到对应关系。