Linux下的文件系统2
2017-03-13
上文针对VFS的基本信息做了介绍,并简单介绍了VFS涉及的几个数据机构,本节结合LInux源码,对各个结构之间的关系进行分析。
一、总体架构图

总体架构图如上图所示,结合进程访问文件的实际情况,根据上图进行细节化的描述。进程通过其结构中的files_struct结构和文件建立联系,看戏files_struct结构
struct files_struct {
  /*
   * read mostly part
   */
    atomic_t count;
    struct fdtable __rcu *fdt;
    struct fdtable fdtab;
  /*
   * written part on a separate cache line in SMP
   */
    spinlock_t file_lock ____cacheline_aligned_in_smp;
    int next_fd;
    unsigned long close_on_exec_init[];
    unsigned long open_fds_init[];
    struct file __rcu * fd_array[NR_OPEN_DEFAULT];
};
首先是一个原子变量,记录打开文件的个数,注意这里不只是普通文件,还包括设备文件等其他文件。next_fd记录当前下一个可用的文件描述符,用于在下次进程打开文件时快速分配。而close_on_exec_init和open_fds_init是位图。fd_array是初始化状态的文件描述符数组,而fdtab是真正管理文件描述符的结构。看下fdtable结构
struct fdtable {
    unsigned int max_fds;
    struct file __rcu **fd;      /* current fd array */
    unsigned long *close_on_exec;
    unsigned long *open_fds;
    struct rcu_head rcu;
};
max_fds表示最大的打开文件数,可以更改。fd是一个指向文件描述符数组的指针,初始化为files_struct结构中fd_array数组的地址,close_on_exec指向files_struct结构中的位域。open_fds是一个指向位域的指针,管理着当前打开的所有描述符,如果位域中的对应位被置位表示该描述符在使用中。
关于描述符表扩展的情况,最后进行解释。上面的描述符表中,都是指向file结构的指针。进程每打开一个文件,就会有一个file结构与之对应。换句话说,file结构记录的某次进程对文件的某一次操作信息。看下file结构
struct file {
    /*
     * fu_list becomes invalid after file_free is called and queued via
     * fu_rcuhead for RCU freeing
     */
    union {
        struct list_head    fu_list;
        struct rcu_head     fu_rcuhead;
    } f_u;
    struct path        f_path;
#define f_dentry    f_path.dentry
    struct inode        *f_inode;    /* cached value */
    const struct file_operations    *f_op;
    /*
     * Protects f_ep_links, f_flags, f_pos vs i_size in lseek SEEK_CUR.
     * Must not be taken from IRQ context.
     */
    spinlock_t        f_lock;
#ifdef CONFIG_SMP
    int            f_sb_list_cpu;
#endif
    atomic_long_t        f_count;
    unsigned int         f_flags;
    fmode_t            f_mode;
    loff_t            f_pos;
    struct fown_struct    f_owner;
    const struct cred    *f_cred;
    struct file_ra_state    f_ra;
    u64            f_version;
#ifdef CONFIG_SECURITY
    void            *f_security;
#endif
    /* needed for tty driver, and maybe others */
    void            *private_data;
#ifdef CONFIG_EPOLL
    /* Used by fs/eventpoll.c to link all the hooks to this file */
    struct list_head    f_ep_links;
    struct list_head    f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
    struct address_space    *f_mapping;
#ifdef CONFIG_DEBUG_WRITECOUNT
    unsigned long f_mnt_write_state;
#endif
};
同一个超级块下打开的所有文件都会通过双链表连接起来。另外,file结构中主要由对应文件的inode缓存,在下次访问不需要通过dentry查找inode了。还有一个path结构,该结构记录当前文件的emulation项和vfsmount结构。其余记录文件的权限模式、读写位置等信息,还有一个重要的函数表,保存操作文件的一些函数的指针。关键是一个进程在这里可以根据dentry查找到inode。究竟是如何查找的呢?看下dentry的结构:
struct dentry {
    /* RCU lookup touched fields */
    unsigned int d_flags;        /* protected by d_lock */
    seqcount_t d_seq;        /* per dentry seqlock */
    struct hlist_bl_node d_hash;    /* lookup hash list */
    struct dentry *d_parent;    /* parent directory */
    struct qstr d_name;
    struct inode *d_inode;        /* Where the name belongs to - NULL is
                     * negative */
    unsigned char d_iname[DNAME_INLINE_LEN];    /* small names */
    /* Ref lookup also touches following */
    unsigned int d_count;        /* protected by d_lock */
    spinlock_t d_lock;        /* per dentry lock */
    const struct dentry_operations *d_op;
    struct super_block *d_sb;    /* The root of the dentry tree */
    unsigned long d_time;        /* used by d_revalidate */
    void *d_fsdata;            /* fs-specific data */
    struct list_head d_lru;        /* LRU list */
    /*
     * d_child and d_rcu can share memory
     */
    union {
        struct list_head d_child;    /* child of parent list */
         struct rcu_head d_rcu;
    } d_u;
    struct list_head d_subdirs;    /* our children */
    struct hlist_node d_alias;    /* inode alias list */
};
dentry结构中有指向当前操作文件的inode指针,父目录的dentry,,当然还包括文件名信息。注意这里文件名并没有作为属性保存在inode节点中,而是保存在dentry结构中。因为文件名对于系统来讲主要来查找inode,而通过dentry可以查找到inode,所以这里其实dentry之后就不需要文件名了。通过dentry还可以定位所属 的超级块。该结构中也有个函数表dentry_operations,主要是针对dentry的操作,如增加、删除dentry。一个目录下的所有子目录会形成一个链表,d_subdirs是链表头。而d_child作为一个节点,连接到父目录的子链表中。上节已经提到,系统中所有的dentry通过一个hash表维护起来,以便于查找。表头是全局变量dentry_hashtable.而对于未使用的dentry,内核使用dentry_unused全局链表来组织。因为每个父目录均会有一条自己子目录的链表,所以系统中还存在一个dentry树。
到目前为止已经找到了具体的inode,inode记录文件的真实属性信息,修改时间,是否是脏,以及在内存中的映射信息。看下inode结构
struct inode {
    umode_t            i_mode;
    unsigned short        i_opflags;
    kuid_t            i_uid;
    kgid_t            i_gid;
    unsigned int        i_flags;
#ifdef CONFIG_FS_POSIX_ACL
    struct posix_acl    *i_acl;
    struct posix_acl    *i_default_acl;
#endif
    const struct inode_operations    *i_op;
    struct super_block    *i_sb;
    struct address_space    *i_mapping;
#ifdef CONFIG_SECURITY
    void            *i_security;
#endif
    /* Stat data, not accessed from path walking */
    unsigned long        i_ino;
    /*
     * Filesystems may only read i_nlink directly.  They shall use the
     * following functions for modification:
     *
     *    (set|clear|inc|drop)_nlink
     *    inode_(inc|dec)_link_count
     */
    union {
        const unsigned int i_nlink;
        unsigned int __i_nlink;
    };
    dev_t            i_rdev;
    loff_t            i_size;
    struct timespec        i_atime;
    struct timespec        i_mtime;
    struct timespec        i_ctime;
    spinlock_t        i_lock;    /* i_blocks, i_bytes, maybe i_size */
    unsigned short          i_bytes;
    unsigned int        i_blkbits;
    blkcnt_t        i_blocks;
#ifdef __NEED_I_SIZE_ORDERED
    seqcount_t        i_size_seqcount;
#endif
    /* Misc */
    unsigned long        i_state;
    struct mutex        i_mutex;
    unsigned long        dirtied_when;    /* jiffies of first dirtying */
    struct hlist_node    i_hash;
    struct list_head    i_wb_list;    /* backing dev IO list */
    struct list_head    i_lru;        /* inode LRU list */
    struct list_head    i_sb_list;
    union {
        struct hlist_head    i_dentry;
        struct rcu_head        i_rcu;
    };
    u64            i_version;
    atomic_t        i_count;
    atomic_t        i_dio_count;
    atomic_t        i_writecount;
    const struct file_operations    *i_fop;    /* former ->i_op->default_file_ops */
    struct file_lock    *i_flock;
    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
    __u32            i_fsnotify_mask; /* all events this inode cares about */
    struct hlist_head    i_fsnotify_marks;
#endif
#ifdef CONFIG_IMA
    atomic_t        i_readcount; /* struct files open RO */
#endif
    void            *i_private; /* fs or device private pointer */
};
inode是一个比较庞大的结构,开头记录了文件的权限信息,如用户、用户组等。该结构中有个函数表inode_operations,记录针对inode的一些操作。inode中还有指向当前文件所属文件系统的超级块结构。当然一个至关重要的就是address_space 类型的i_mapping指针了。其指向一个address_space 结构,记录当前文件在内存中的映射情况。这点等会在分析。除此之外,记录文件的一些时间信息。前文说过,inode在内存中有三种类型:位于内存中但未使用的;位于内存中正在使用的;位于内存中已经发生变化即需要写会到磁盘的,前两种都是全局链表,第三种特定于超级块结构。除此之外,inode还在一个hash表中出现,表头是inode_hashtable,支持根据inode编号和超级块快速访问inode。
到此进程已经找到了具体的inode节点,后来又是如何把文件映射到内存中呢?一个核心结构就是address_space,先看下该结构
struct address_space {
    struct inode        *host;        /* owner: inode, block_device */
    struct radix_tree_root    page_tree;    /* radix tree of all pages */
    spinlock_t        tree_lock;    /* and lock protecting it */
    unsigned int        i_mmap_writable;/* count VM_SHARED mappings */
    struct rb_root        i_mmap;        /* tree of private and shared mappings */
    struct list_head    i_mmap_nonlinear;/*list VM_NONLINEAR mappings */
    struct mutex        i_mmap_mutex;    /* protect tree, count, list */
    /* Protected by tree_lock together with the radix tree */
    unsigned long        nrpages;    /* number of total pages */
    pgoff_t            writeback_index;/* writeback starts here */
    const struct address_space_operations *a_ops;    /* methods */
    unsigned long        flags;        /* error bits/gfp mask */
    struct backing_dev_info *backing_dev_info; /* device readahead, etc */
    spinlock_t        private_lock;    /* for use by the address_space */
    struct list_head    private_list;    /* ditto */
    void            *private_data;    /* ditto */
} __attribute__((aligned(sizeof(long))));
该结构特定于inode节点存在,多个进程可以共享同一个文件,所以在特定于进程的file结构中,有一个指向该inode address_space的指针f_mapping。具体的访问位置记录在file结构中。address_space仅仅负责对文件的映射,该结构管理了对应文件映射的所有内存区域vm_area_struct实例。上面的i_map作为一个红黑树根,关联所有的vm_area_struct,而i_mmap_nonliner是一个双向链表,关联所有非线性映射的vm_area_struct实例。该结构中还记录了所属inode节点的指针host,区域包含的虚拟页面的数量nrpages,当然还有一组操作函数,用于和设备交互,如读取一个页或者写入一个页,设置页面为脏等。关于进程虚拟内存的管理,参考另一篇文章:
Linux下的文件系统2的更多相关文章
- 全面了解Linux下Proc文件系统
		
全面了解Linux下Proc文件系统 Proc是一个虚拟文件系统,在Linux系统中它被挂载于/proc目录之上.Proc有多个功能 ,这其中包括用户可以通过它访问内核信息或用于排错,这其中一个非 ...
 - Linux 下EXT2文件系统 —— 如何将蚂蚁和大象优雅的装进冰箱里
		
这一阵子真是偷懒,无时无刻不和自己身体中的懒癌做斗争.最终我还是被打败了,星期天两天几乎都是荒废过去的,在空闲的时候实际上我内心也是有点焦虑的,不知道去怎么度过这时间.学习吧又不想学习,看电视娱乐吧也 ...
 - linux下的文件系统
		
转http://www.cnblogs.com/yyyyy5101/articles/1901842.html 谈谈个人对于文件系统的认识,其实这也体现了计算机操作系统的抽象:你不用管计算机中的文件如 ...
 - Linux下删除文件系统空间不释放的问题
		
删除了Linux下的一个文件,但是系统空间并没有被释放. 如下:/home/hadmin/data/hadoop 使用了1.3T的空间,但是实际只使用了600多G 原因是我删除了一个600多G的文件, ...
 - <解说linux下proc文件系统>
		
proc文件系统的作用是访问系统内核信息 proc不是一个真实的文件系统,它不占系统的外存空间,只是以文件的形式为用户访问linux内核数据提供接口,因为系统内核总是动态的变化,所以我们所捕捉到的也只 ...
 - linux下查看文件系统类型
		
1. df -hT命令 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G) -T, --pr ...
 - Linux下查看文件系统磁盘使用
		
[root@localhost ~]# df -h 可以查看所有文件系统的磁盘使用情况 du --max-depth=1 -h 可以查看当前目录下各子目录的磁盘使用情况 参考:http://www.2 ...
 - Linux下网络文件系统NFS服务搭建易错点总结
		
一.环境准备: 1 [root@czh ~]# cat /etc/redhat-release 2 CentOS release 6.7 (Final) 3 [root@czh ~]# uname - ...
 - Linux下识别分区文件系统类型
		
Linux下挂载文件系统有时候需要填写文件系统.但有的设备拿到手还不知道文件系统,这种情况,可以用 parted命令 # parted /dev/vda GNU Parted 3.2 Using /d ...
 
随机推荐
- IDEA使用笔记(十)——设置Java方法注释
			
如果你看到了,这篇博文,那么你是幸运的!你问什么?你百度百度同类型的网文就明白了! 一:先看效果 二:我的实验过程(肯定还有别的方式) 1:新建 Template Group,详细操作步骤见下图 ...
 - asp.net mvc 微信支付代码分析(根据沐雪微信平台3.1商城业务来分析)
			
开发微信应用,微信支付是永远要面对的.现在的微信支付相对以往已经很稳定,很少出现诡异情况.再加上无数人开发的经验分享,现在开发微信支付已经没什么难度了. 我这次主要是想基于沐雪微信平台的微商城业务来分 ...
 - [转]剑指offer之Java源代码
			
一.引言 <剑指offer>可谓是程序猿面试的神书了,在面试中帮了我很多,大部分面试的算法题都会遇到原题或者是类似的题.但是书上的代码都是C版的,我在这里整理了一份Java版的代码供大家学 ...
 - 微信小程序——动态设置swiper的高度
			
根据小程序的设定,swiper组件默认高度为150px,无法根据内容来撑高.如果里面的内容固定还好说,直接设置一个高度就可以了.要是里面内容是动态变化的,这个特性使得我们使用这个组件的时候感到诸多不便 ...
 - win8.1系统出现C0000034正在应用更新操作怎么办
			
说来也奇怪,笔者Dell台式机前几天系统提示有更新,笔者对系统进行了更新,可昨天开机后,就出现了C0000034正在应用更新操作的情况,且电脑一直没反应,上网搜了一下帖子,发现复制粘贴的帖子好多,基本 ...
 - ubuntu下core file文件生成及调试
			
1.简介:corefile 是Linux下程序崩溃时生成的文件,可以用来分析程序崩溃的原因,因为它内部包含了程序崩溃时的堆栈信息. 2.corefile的设置 默认情况下,程序崩溃是不会生成coref ...
 - bootstarpTable  load data
			
<!doctype html> <html lang="en"> <head> <!-- Required meta tags --> ...
 - linux telnet检测与某个端口是否开通
			
转自:http://blog.51cto.com/meiling/1982402 一:telnet此法常被用来检测是个远端端口是否通畅. 测试域名: # telnet baidu.com 80 Try ...
 - java发送邮件无法显示图片 图裂 的解决办法
			
package com.thinkgem.jeesite.common.utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; ...
 - JS设计模式——工厂模式详解
			
它的领域中同其它模式的不同之处在于它并没有明确要求我们使用一个构造器.取而代之,一个工厂能提供一个创建对象的公共接口,我们可以在其中指定我们希望被创建的工厂对象的类型. 简单工厂模式:使用一个类(通常 ...