APUE——文件系统的VFS以及相关结构体简要分析

参考链接如下:
https://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/#fig2
https://blog.csdn.net/Ohmyberry/article/details/80427492
https://blog.csdn.net/shanshanpt/article/details/38943731
https://www.cnblogs.com/LittleHann/p/3865490.html
https://www.cnblogs.com/LittleHann/p/4305892.html
https://www.cnblogs.com/fengkang1008/p/4691231.html
https://www.cnblogs.com/linux-xin/p/8126999.html

1. 图示文件系统

1.1经典的文件系统

在这里插入图片描述
需要注意几点:

  1. dentry 本身目录块也属于数据块
  2. inode指向实际block,其属于i节点区
  3. 目录块本身包含inode指针和文件名

1.2 对文件系统结构体分析

精力有限,凑合看看,结构体很长,选了一些,例如:
标准IO的fread函数, size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) ,实际调用read,其中需要注意FILE 与 fd 的差异,fd为fd_array的数组索引号,实际存放file指针,通过file指针调用f_op的函数
在这里插入图片描述
在这里插入图片描述

2. VFS

如下两张图能直观反映Linux下文件系统中VFS所起的作用,上接系统调用,下接真实文件系统,类似于autosar里的RTE,使得不同的文件系统在Linux里共存,且同一个API可以完成对文件系统的操作。
在这里插入图片描述在这里插入图片描述
VFS抽象了文件、目录项 (dentry)、索引节点 (inode) 及挂载点,其主要有如下四个部分组成:

  1. 超级块对象 (superblock object)
  2. 索引节点对象 (inode object)、
  3. 目录项对象 (dentry object) 、
  4. 文件对象 (file object)

上述四个模块在VFS中联系如下图所示:
在这里插入图片描述

2.1 superblock

超级块就是对所有文件系统的管理机构,主要管理block和inode等,记录保存文件系统的类型、大小、状态等等,每种文件系统都要把自己的信息挂到super_blocks这么一个全局链表上,超级块与文件系统一一对应,同一个文件系统类型可能含有多个超级块。

  1. block 与inode 的总量;
  2. 未使用与已使用的inode / block 数量;
  3. block 与inode 的大小(block 为1, 2, 4K,inode 为128bytes 或256bytes);
  4. filesystem 的挂载时间、最近一次写入资料的时间、最近一次检验磁盘(fsck) 的时间等档案系统的相关信息;
  5. 一个valid bit 数值,若此档案系统已被挂载,则valid bit 为0 ,若未被挂载,则valid bit 为1 。
    “文件系统”和“文件系统类型”不一样!一个文件系统类型下可以包括很多文件系统即很多的super_block

具体代码和解释部分,参考https://www.cnblogs.com/LittleHann/p/3865490.html

struct super_block 
{
    /* 
    Keep this first 
    指向超级块链表的指针,用于将系统中所有的超级块聚集到一个链表中,该链表的表头是全局变量super_blocks
    */
    struct list_head    s_list;

    /* 
    search index; _not_ kdev_t 
    设备标识符
    */        
    dev_t            s_dev;        

    //以字节为单位的块大小
    unsigned long        s_blocksize;

    //以位为单位的块大小
    unsigned char        s_blocksize_bits;

    //修改脏标志,如果以任何方式改变了超级块,需要向磁盘回写,都会将s_dirt设置为1,否则为0
    unsigned char        s_dirt;

    //文件大小上限 Max file size
    loff_t            s_maxbytes;     

    //文件系统类型
    struct file_system_type    *s_type; 

    /*
    struct super_operations 
    {
        //给定的超级块下创建和初始化一个新的索引节点对象; 
        struct inode *(*alloc_inode)(struct super_block *sb);

        //用于释放给定的索引节点; 
        void (*destroy_inode)(struct inode *);

        //VFS在索引节点脏(被修改)时会调用此函数,日志文件系统(如ext3,ext4)执行该函数进行日志更新; 
        void (*dirty_inode) (struct inode *);

        //用于将给定的索引节点写入磁盘,wait参数指明写操作是否需要同步; 
        int (*write_inode) (struct inode *, struct writeback_control *wbc);

        //在最后一个指向索引节点的引用被释放后,VFS会调用该函数,VFS只需要简单地删除这个索引节点后,普通Uinx文件系统就不会定义这个函数了;
        void (*drop_inode) (struct inode *);

        //用于从磁盘上删除给定的索引节点; 
        void (*delete_inode) (struct inode *);

        //在卸载文件系统时由VFS调用,用来释放超级块,调用者必须一直持有s_lock锁;
        void (*put_super) (struct super_block *);

        //用给定的超级块更新磁盘上的超级块。VFS通过该函数对内存中的超级块和磁盘中的超级块进行同步。调用者必须一直持有s_lock锁; 
        void (*write_super) (struct super_block *);

        //使文件系统的数据元与磁盘上的文件系统同步。wait参数指定操作是否同步; 
        int (*sync_fs)(struct super_block *sb, int wait);
        int (*freeze_fs) (struct super_block *);
        int (*unfreeze_fs) (struct super_block *);

         //VFS通过调用该函数获取文件系统状态。指定文件系统县官的统计信息将放置在statfs中; 
        int (*statfs) (struct dentry *, struct kstatfs *);

        //当指定新的安装选项重新安装文件系统时,VFS会调用该函数。调用者必须一直持有s_lock锁; 
        int (*remount_fs) (struct super_block *, int *, char *);

        //VFS调用该函数释放索引节点,并清空包含相关数据的所有页面; 
        void (*clear_inode) (struct inode *);

        //VFS调用该函数中断安装操作。该函数被网络文件系统使用,如NFS; 
        void (*umount_begin) (struct super_block *);

        int (*show_options)(struct seq_file *, struct vfsmount *);
        int (*show_stats)(struct seq_file *, struct vfsmount *);
        #ifdef CONFIG_QUOTA
        ssize_t (*quota_read)(struct super_block *,
        int, char *, size_t, loff_t);
        ssize_t (*quota_write)(struct super_block *,
        int, const char *, size_t, loff_t);
        #endif
        int (*bdev_try_to_free_page)(struct super_block*,
        struct page*, gfp_t);
    };
    */
    const struct super_operations    *s_op;

    //磁盘限额方法
    const struct dquot_operations    *dq_op;

    //磁盘限额方法
    const struct quotactl_ops    *s_qcop;

    //导出方法
    const struct export_operations *s_export_op;

    //挂载标志 
    unsigned long        s_flags;

    //文件系统魔数
    unsigned long        s_magic;

    //目录挂载点,s_root将超级块与全局根目录的dentry项关联起来,只有通常可见的文件系统的超级块,才指向/(根)目录的dentry实例。具有特殊功能、不出现在通常的目录层次结构中的文件系统(例如管道或套接字文件系统),指向专门的项,不能通过普通的文件命令访问。处理文件系统对象的代码经常需要检查文件系统是否已经装载,而s_root可用于该目的,如果它为NULL,则该文件系统是一个伪文件系统,只在内核内部可见。否则,该文件系统在用户空间中是可见的
    struct dentry        *s_root;

    //卸载信号量
    struct rw_semaphore    s_umount;

    //超级块信号量
    struct mutex        s_lock;

    //引用计数
    int            s_count;

    //尚未同步标志
    int            s_need_sync;

    //活动引用计数
    atomic_t        s_active;
#ifdef CONFIG_SECURITY
    //安全模块
    void                    *s_security;
#endif
    struct xattr_handler    **s_xattr;

    //all inodes 
    struct list_head    s_inodes;    

    //匿名目录项 anonymous dentries for (nfs) exporting 
    struct hlist_head    s_anon;        

    //被分配文件链表,列出了该超级块表示的文件系统上所有打开的文件。内核在卸载文件系统时将参考该列表,如果其中仍然包含为写入而打开的文件,则文件系统仍然处于使用中,卸载操作失败,并将返回适当的错误信息
    struct list_head    s_files;

    /* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */
    struct list_head    s_dentry_lru; 

    //unused dentry lru of dentry on lru 
    int            s_nr_dentry_unused;

    //指向了底层文件系统的数据所在的相关块设备
    struct block_device    *s_bdev;
    struct backing_dev_info *s_bdi;
    struct mtd_info        *s_mtd;

    //该类型文件系统
    struct list_head    s_instances;

    //限额相关选项 Diskquota specific options 
    struct quota_info    s_dquot;     

    int            s_frozen;
    wait_queue_head_t    s_wait_unfrozen;

    //文本名字 Informational name 
    char s_id[32];                 

    //Filesystem private info 
    void             *s_fs_info;
    fmode_t            s_mode;

    /*
     * The next field is for VFS *only*. No filesystems have any business
     * even looking at it. You had been warned.
     */
    struct mutex s_vfs_rename_mutex;    /* Kludge */

    /* Granularity of c/m/atime in ns. Cannot be worse than a second 指定了文件系统支持的各种时间戳的最大可能的粒度 */
    u32           s_time_gran;

    /*
     * Filesystem subtype.  If non-empty the filesystem type field
     * in /proc/mounts will be "type.subtype"
     */
    char *s_subtype;

    /*
     * Saved mount options for lazy filesystems using
     * generic_show_options()
     */
    char *s_options;
};

2.2 inode

inode保存着文件大小,设备标识符,用户标识符,用户组标识符,文件模式,扩展属性,文件读取或修改的时间戳,链接数量,指向存储该内容的磁盘区块的指针,文件分类等等。

inode分磁盘inode和vfs内存inode,内核会把磁盘inode读入内存,加上点东西形成内存inode,i_links_count为磁盘inode计数,i_nlink为内存inode计数。

inode 号(inode 是文件元数据的一部分但其并不包含文件名,inode 号即索引节点号)是文件的唯一标识而非文件名。文件名仅是为了方便人们的记忆和使用,系统或程序通过 inode 号寻找正确的文件数据块。

inode需要注意两个参数,i_count 与 i_nlink,i_count 为引用计数,i_nlink为硬链接计数,每个dentry都有一个唯一的inode,而每个inode则可能有多个dentry,由于硬链接是有着相同 inode 号仅文件名不同的文件,因此硬链接存在以下几点特性:

  1. 文件有相同的 inode 及 data block;
  2. 只能对已存在的文件进行创建;
  3. 不能交叉文件系统进行硬链接的创建;
  4. 不能对目录进行创建,只可对文件创建;
  5. 删除一个硬链接文件并不影响其他有相同 inode 号的文件。
    软链接与硬链接不同,若文件用户数据块中存放的内容是另一文件的路径名的指向,则该文件就是软连接
    在这里插入图片描述
struct inode 
{    
    /*
    哈希表 
    */
    struct hlist_node    i_hash;

    /*
    索引节点链表(backing dev IO list)
    */
    struct list_head    i_list;     
    struct list_head    i_sb_list;

    /*
    目录项链表
    */
    struct list_head    i_dentry;

    /*
    节点号
    */
    unsigned long        i_ino;

    /*
    引用记数
    */
    atomic_t        i_count;

    /*
    硬链接数
    */
    unsigned int        i_nlink;

    /*
    使用者id
    */
    uid_t            i_uid;

    /*
    使用者所在组id
    */
    gid_t            i_gid;

    /*
    实设备标识符
    */
    dev_t            i_rdev;

    /*
    版本号
    */
    u64            i_version;

    /*
    以字节为单位的文件大小
    */
    loff_t            i_size;
#ifdef __NEED_I_SIZE_ORDERED
    seqcount_t        i_size_seqcount;
#endif
    /*
    最后访问时间
    */
    struct timespec        i_atime;

    /*
    最后修改(modify)时间
    */
    struct timespec        i_mtime;

    /*
    最后改变(change)时间
    */
    struct timespec        i_ctime;

    /*
    文件的块数
    */
    blkcnt_t        i_blocks;

    /*
    以位为单位的块大小
    */ 
    unsigned int        i_blkbits;
    
    /*
    使用的字节数
    */
    unsigned short          i_bytes;

    /*
    访问权限控制
    */
    umode_t            i_mode;
    
    /*
    自旋锁 
    */
    spinlock_t        i_lock;     
    struct mutex        i_mutex;

    /*
    索引节点信号量
    */
    struct rw_semaphore    i_alloc_sem;

    /*
    索引节点操作表
    索引节点的操作inode_operations定义在linux/fs.h
    struct inode_operations 
    {
        /*
        1. VFS通过系统调用create()和open()来调用该函数,从而为dentry对象创建一个新的索引节点。在创建时使用mode制定初始模式
        */
        int (*create) (struct inode *, struct dentry *,int); 
        /*
        2. 该函数在特定目录中寻找索引节点,该索引节点要对应于dentry中给出的文件名
        */
        struct dentry * (*lookup) (struct inode *, struct dentry *); 
        /*
        3. 该函数被系统调用link()调用,用来创建硬连接。硬链接名称由dentry参数指定,连接对象是dir目录中ld_dentry目录想所代表的文件
        */
        int (*link) (struct dentry *, struct inode *, struct dentry *); 
        /*
        4. 该函数被系统调用unlink()调用,从目录dir中删除由目录项dentry制动的索引节点对象
        */
        int (*unlink) (struct inode *, struct dentry *); 
        /*
        5. 该函数被系统调用symlik()调用,创建符号连接,该符号连接名称由symname指定,连接对象是dir目录中的dentry目录项
        */
        int (*symlink) (struct inode *, struct dentry *, const char *); 
        /*
        6. 该函数被mkdir()调用,创建一个新路径。创建时使用mode制定的初始模式
        */
        int (*mkdir) (struct inode *, struct dentry *, int); 
        /*
        7. 该函数被系统调用rmdir()调用,删除dir目录中的dentry目录项代表的文件
        */
        int (*rmdir) (struct inode *, struct dentry *); 
        /*
        8. 该函数被系统调用mknod()调用,创建特殊文件(设备文件、命名管道或套接字)。要创建的文件放在dir目录中,其目录项问dentry,关联的设备为rdev,初始权限由mode指定
        */
        int (*mknod) (struct inode *, struct dentry *, int, dev_t); 
        /*
        9. VFS调用该函数来移动文件。文件源路径在old_dir目录中,源文件由old_dentry目录项所指定,目标路径在new_dir目录中,目标文件由new_dentry指定
        */
        int (*rename) (struct inode *, struct dentry *, struct inode *, struct dentry *); 
        /*
        10. 该函数被系统调用readlink()调用,拷贝数据到特定的缓冲buffer中。拷贝的数据来自dentry指定的符号链接,最大拷贝大小可达到buflen字节
        */
        int (*readlink) (struct dentry *, char *, int); 
        /*
        11. 该函数由VFS调用,从一个符号连接查找他指向的索引节点,由dentry指向的连接被解析
        */
        int (*follow_link) (struct dentry *, struct nameidata *); 
        /*
        12. 在follow_link()调用之后,该函数由vfs调用进行清楚工作
        */
        int (*put_link) (struct dentry *, struct nameidata *); 
        /*
        13. 该函数由VFS调用,修改文件的大小,在调用之前,索引节点的i_size项必须被设置成预期的大小
        */
        void (*truncate) (struct inode *);
        
        /*
        该函数用来检查inode所代表的文件是否允许特定的访问模式,如果允许特定的访问模式,返回0,否则返回负值的错误码。多数文件系统都将此区域设置为null,使用VFS提供的通用方法进行检查,这种检查操作仅仅比较索引
及诶但对象中的访问模式位是否和mask一致,比较复杂的系统, 比如支持访问控制链(ACL)的文件系统,需要使用特殊的permission()方法
        */
        int (*permission) (struct inode *, int);
        
        /*
        该函数被notify_change调用,在修改索引节点之后,通知发生了改变事件
        */
        int (*setattr) (struct dentry *, struct iattr *);
        
        /*
        在通知索引节点需要从磁盘中更新时,VFS会调用该函数
        */
        int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *);
        
        /*
        该函数由VFS调用,向dentry指定的文件设置扩展属性,属性名为name,值为value
        */
        int (*setxattr) (struct dentry *, const char *, const void *, size_t, int);
        
        /*
        该函数被VFS调用,向value中拷贝给定文件的扩展属性name对应的数值
        */
        ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
        
        /*
        该函数将特定文件所有属性别表拷贝到一个缓冲列表中
        */
        ssize_t (*listxattr) (struct dentry *, char *, size_t);
        
        /*
        该函数从给定文件中删除指定的属性
        */
        int (*removexattr) (struct dentry *, const char *);        
    };
    */
    const struct inode_operations    *i_op;

    /*
    默认的索引节点操作
    former ->i_op->default_file_ops
    */
    const struct file_operations    *i_fop;     

    /*
    相关的超级块
    */
    struct super_block    *i_sb;

    /*
    文件锁链表
    */
    struct file_lock    *i_flock;

    /*
    相关的地址映射
    */
    struct address_space    *i_mapping;

    /*
    设备地址映射
  address_space结构与文件的对应:一个具体的文件在打开后,内核会在内存中为之建立一个struct inode结构,其中的i_mapping域指向一个address_space结构。这样,一个文件就对应一个address_space结构,一个 address_space与一个偏移量能够确定一个page cache 或swap cache中的一个页面。因此,当要寻址某个数据时,很容易根据给定的文件及数据在文件内的偏移量而找到相应的页面
    */
    struct address_space    i_data;
#ifdef CONFIG_QUOTA
    /*
    节点的磁盘限额
    */
    struct dquot        *i_dquot[MAXQUOTAS];
#endif
    /*
    块设备链表
    */
    struct list_head    i_devices;
    union 
    {
        //管道信息
        struct pipe_inode_info    *i_pipe;
        //块设备驱动
        struct block_device    *i_bdev;
        struct cdev        *i_cdev;
    };
    
    /*
    索引节点版本号
    */
    __u32            i_generation;

#ifdef CONFIG_FSNOTIFY
    /*
    目录通知掩码
    all events this inode cares about
    */
    __u32            i_fsnotify_mask;  
    struct hlist_head    i_fsnotify_mark_entries; /* fsnotify mark entries */
#endif

#ifdef CONFIG_INOTIFY
    struct list_head    inotify_watches; /* watches on this inode */
    struct mutex        inotify_mutex;    /* protects the watches list */
#endif
    /*
    状态标志
    */
    unsigned long        i_state;

    /*
    首次修改时间
    jiffies of first dirtying
    */
    unsigned long        dirtied_when;     
    
    /*
    文件系统标志
    */
    unsigned int        i_flags;

    /*
    写者记数
    */
    atomic_t        i_writecount;
#ifdef CONFIG_SECURITY
    /*
    安全模块
    */
    void            *i_security;
#endif
#ifdef CONFIG_FS_POSIX_ACL
    struct posix_acl    *i_acl;
    struct posix_acl    *i_default_acl;
#endif
    void            *i_private; /* fs or device private pointer */
};

2.3 dentry 目录

目录项是描述文件的逻辑属性,只存在于内存中,并没有实际对应的磁盘上的描述,更确切的说是存在于内存的目录项缓存,为了提高查找性能而设计。注意不管是文件夹还是最终的文件,都是属于目录项,所有的目录项在一起构成一颗庞大的目录树。例如:open一个文件/home/xxx/yyy.txt,那么/、home、xxx、yyy.txt都是一个目录项,VFS在查找的时候,根据一层一层的目录项找到对应的每个目录项的inode,那么沿着目录项进行操作就可以找到最终的文件。
目录也是一种文件(所以也存在对应的inode)

struct dentry 
{
    //目录项引用计数器 
    atomic_t d_count;

    /*
    目录项标志 protected by d_lock 
    #define DCACHE_AUTOFS_PENDING 0x0001    // autofs: "under construction"  
    #define DCACHE_NFSFS_RENAMED  0x0002    // this dentry has been "silly renamed" and has to be eleted on the last dput() 
    #define    DCACHE_DISCONNECTED 0x0004        //指定了一个dentry当前没有连接到超级块的dentry树
    #define DCACHE_REFERENCED    0x0008      //Recently used, don't discard.  
    #define DCACHE_UNHASHED        0x0010        //该dentry实例没有包含在任何inode的散列表中
    #define DCACHE_INOTIFY_PARENT_WATCHED    0x0020 // Parent inode is watched by inotify 
    #define DCACHE_COOKIE        0x0040        // For use by dcookie subsystem 
    #define DCACHE_FSNOTIFY_PARENT_WATCHED    0x0080 // Parent inode is watched by some fsnotify listener 
    */
    unsigned int d_flags;    

    //per dentry lock    
    spinlock_t d_lock;        

    //当前dentry对象表示一个装载点,那么d_mounted设置为1,否则为0
    int d_mounted;

    /*
    文件名所属的inode,如果为NULL,则表示不存在的文件名
    如果dentry对象是一个不存在的文件名建立的,则d_inode为NULL指针,这有助于加速查找不存在的文件名,通常情况下,这与查找实际存在的文件名同样耗时
    */
    struct inode *d_inode;         
    /*
    The next three fields are touched by __d_lookup.  Place them here so they all fit in a cache line.
    */
    //用于查找的散列表 lookup hash list 
    struct hlist_node d_hash;    

    /*
    指向当前的dentry实例的父母了的dentry实例 parent directory
    当前的dentry实例即位于父目录的d_subdirs链表中,对于根目录(没有父目录),d_parent指向其自身的dentry实例
    */  
    struct dentry *d_parent;

    /*
    d_iname指定了文件的名称,qstr是一个内核字符串的包装器,它存储了实际的char*字符串以及字符串长度和散列值,这使得更容易处理查找工作
    要注意的是,这里并不存储绝对路径,而是只有路径的最后一个分量,例如对/usr/bin/emacs只存储emacs,因为在linux中,路径信息隐含在了dentry层次链表结构中了
    */    
    struct qstr d_name;

    //LRU list
    struct list_head d_lru;        
    /*
     * d_child and d_rcu can share memory
     */
    union 
    {
        /* child of parent list */
        struct list_head d_child;
        //链表元素,用于将dentry连接到inode的i_dentry链表中    
         struct rcu_head d_rcu;
    } d_u;

    //our children 子目录/文件的目录项链表
    struct list_head d_subdirs;    

    /*
    inode alias list 链表元素,用于将dentry连接到inode的i_dentry链表中 
    d_alias用作链表元素,以连接表示相同文件的各个dentry对象,在利用硬链接用两个不同名称表示同一文件时,会发生这种情况,对应于文件的inode的i_dentry成员用作该链表的表头,各个dentry对象通过d_alias连接到该链表中
    */
    struct list_head d_alias;    

    //used by d_revalidate 
    unsigned long d_time;

    /*
    d_op指向一个结构,其中包含了各种函数指针,提供对dentry对象的各种操作,这些操作必须由底层文件系统实现
    struct dentry_operations 
    {
        //在把目录项对象转换为一个文件路径名之前,判定该目录项对象是否依然有效
        int (*d_revalidate)(struct dentry *, struct nameidata *);

        //生成一个散列值,用于目录项散列表
        int (*d_hash) (struct dentry *, struct qstr *);
        
        //比较两个文件名
        int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);

        //当对目录项对象的最后一个引用被删除,调用该方法
        int (*d_delete)(struct dentry *);

        //当要释放一个目录项对象时,调用该方法
        void (*d_release)(struct dentry *);

        //当一个目录对象变为负状态时,调用该方法
        void (*d_iput)(struct dentry *, struct inode *);
        char *(*d_dname)(struct dentry *, char *, int);
    };
    */        
    const struct dentry_operations *d_op;

    //The root of the dentry tree dentry树的根,超级块
    struct super_block *d_sb;    

    //fs-specific data 特定文件系统的数据
    void *d_fsdata;            

    /*
    短文件名small names存储在这里
    如果文件名由少量字符组成,则只保存在d_iname中,而不是dnanme中,用于加速访问
    */ 
    unsigned char d_iname[DNAME_INLINE_LEN_MIN];    
};

2.4 file结构

件结构体代表一个打开的文件,系统中的每个打开的文件在内核空间都有一个关联的struct file。它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数。在文件的所有实例都关闭后,内核释放这个数据结构

struct file 
{
    /*
     * fu_list becomes invalid after file_free is called and queued via
     * fu_rcuhead for RCU freeing
     */
    union 
    {
        /*
        定义在 linux/include/linux/list.h中 
        struct list_head 
        {
            struct list_head *next, *prev;
        };
        用于通用文件对象链表的指针,所有打开的文件形成一个链表
        */
        struct list_head    fu_list;
        /*
        定义在linux/include/linux/rcupdate.h中  
        struct rcu_head 
        {
            struct rcu_head *next;
            void (*func)(struct rcu_head *head);
        };
        RCU(Read-Copy Update)是Linux 2.6内核中新的锁机制
        */
        struct rcu_head     fu_rcuhead;
    } f_u;
    
    /*
    定义在linux/include/linux/namei.h中
    struct path 
    {
        /*
        struct vfsmount *mnt的作用是指出该文件的已安装的文件系统,即指向VFS安装点的指针
        */
        struct vfsmount *mnt;
        /*
        struct dentry *dentry是与文件相关的目录项对象,指向相关目录项的指针
        */
        struct dentry *dentry;
    };
    */
    struct path        f_path;
#define f_dentry    f_path.dentry
#define f_vfsmnt    f_path.mnt

    /*
   指向文件操作表的指针
    定义在linux/include/linux/fs.h中,其中包含着与文件关联的操作,例如
    struct file_operations 
    {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    int (*readdir) (struct file *, void *, filldir_t);
    unsigned int (*poll) (struct file *, struct poll_table_struct *);
    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, struct dentry *, int datasync);
    int (*aio_fsync) (struct kiocb *, int datasync);
    int (*fasync) (int, struct file *, int);
    int (*lock) (struct file *, int, struct file_lock *);
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
    int (*check_flags)(int);
    int (*flock) (struct file *, int, struct file_lock *);
    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
    int (*setlease)(struct file *, long, struct file_lock **);
     };
    当打开一个文件时,内核就创建一个与该文件相关联的struct file结构,其中的*f_op就指向的是具体对该文件进行操作的函数
    例如用户调用系统调用read来读取该文件的内容时,那么系统调用read最终会陷入内核调用sys_read函数,而sys_read最终会调用于该文件关联的struct file结构中的f_op->read函数对文件内容进行读取
    */
    const struct file_operations    *f_op;
    spinlock_t        f_lock;  /* f_ep_links, f_flags, no IRQ */

    /*
    typedef struct { volatile int counter; } atomic_t;
    volatile修饰字段告诉gcc不要对该类型的数据做优化处理,对它的访问都是对内存的访问,而不是对寄存器的访问
    f_count的作用是记录对文件对象的引用计数,也即当前有多少个进程在使用该文件
    */
    atomic_long_t        f_count;

    /*
    当打开文件时指定的标志,对应系统调用open的int flags参数。驱动程序为了支持非阻塞型操作需要检查这个标志
    */
    unsigned int         f_flags;

    /*
    对文件的读写模式,对应系统调用open的mod_t mode参数。如果驱动程序需要这个值,可以直接读取这个字段。
    mod_t被定义为:
    typedef unsigned int __kernel_mode_t;
    typedef __kernel_mode_t         mode_t;
    */
    fmode_t            f_mode;

    /*
    当前的文件指针位置,即文件的读写位置
    loff_t被定义为:
    typedef long long       __kernel_loff_t;
    typedef __kernel_loff_t         loff_t;
    */
    loff_t            f_pos;

    /*
    struct fown_struct在linux/include/linux/fs.h被定义 
    struct fown_struct 
    {
        rwlock_t lock;          /* protects pid, uid, euid fields */
        struct pid *pid;        /* pid or -pgrp where SIGIO should be sent */
        enum pid_type pid_type; /* Kind of process group SIGIO should be sent to */
        uid_t uid, euid;        /* uid/euid of process setting the owner */
        int signum;             /* posix.1b rt signal to be delivered on IO */
    };
    该结构的作用是通过信号进行I/O时间通知的数据
    */
    struct fown_struct    f_owner;
    const struct cred    *f_cred;

    /*
    struct file_ra_state结构被定义在/linux/include/linux/fs.h中 
    struct file_ra_state 
    {
        pgoff_t start;                  /* where readahead started */
        unsigned long size;             /* # of readahead pages */
        unsigned long async_size;       /* do asynchronous readahead when
                           there are only # of pages ahead */
                           
        unsigned long ra_pages;         /* Maximum readahead window */
        unsigned long mmap_hit;         /* Cache hit stat for mmap accesses */
        unsigned long mmap_miss;        /* Cache miss stat for mmap accesses */
        unsigned long prev_index;       /* Cache last read() position */
        unsigned int prev_offset;       /* Offset where last read() ended in a page */
    };
    该结构标识了文件预读状态,文件预读算法使用的主要数据结构,当打开一个文件时,f_ra中出了perv_page(默认为-1)ra_apges(对该文件允许的最大预读量)这两个字段外,其他的所有西端都置为0
    */
    struct file_ra_state    f_ra;
    
    /*
    记录文件的版本号,每次使用后都自动递增
    */
    u64            f_version;
#ifdef CONFIG_SECURITY
    /*
    #ifdef CONFIG_SECURITY
        void                    *f_security;
    #endif
    如果在编译内核时配置了安全措施,那么struct file结构中就会有void *f_security数据项,用来描述安全措施或者是记录与安全有关的信息。
    */
    void            *f_security;
#endif
    /*
    系统在调用驱动程序的open方法前将这个指针置为NULL。驱动程序可以将这个字段用于任意目的,也可以忽略这个字段。驱动程序可以用这个字段指向已分配的数据,但是一定要在内核释放file结构前的release方法中清除它
    */
    void            *private_data;

#ifdef CONFIG_EPOLL
    /*
    被用在fs/eventpoll.c来链接所有钩到这个文件上。其中
        1) f_ep_links是文件的事件轮询等待者链表的头
        2) f_ep_lock是保护f_ep_links链表的自旋锁
    */
    struct list_head    f_ep_links;
    struct list_head    f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */

    /*
    struct address_space被定义在/linux/include/linux/fs.h中,此处是指向文件地址空间的指针 
    */
    struct address_space    *f_mapping;
#ifdef CONFIG_DEBUG_WRITECOUNT
    unsigned long f_mnt_write_state;
#endif
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章