Linux文件系统(二)—— VFS

【前言】

        文件系统是结合了众多工程师智慧的结晶,所以在这个过程中,会出现各种各样的小技术手段来解决一些实际性问题,从而产生了很多技术词汇。

【文件系统Overview】

Linux一切皆文件:普通文件、目录文件(也就是文件夹)、设备文件、链接文件、管道文件、套接字文件(数据通信的接口)等等。引入文件系统,帮助我们屏蔽底层实现的细节(不必care扇区,cache,文件管理),我们只需通过VFS提供的fs_op就可以对文件进行对应的操作。

Android 支持的文件系统:        ext4  vfat  f2fs  sdcardfs  fuse(fuse解决了文件系统必须在内核态的难题。将文件系统的实现从内核态搬到了用户态

 Android linux 文件系统核心:   VFS(1.向上提供接口,向下兼容文件系统,实现了inode/page cache等公共部分,让其他文件系统无需重复实现)

【文件系统四大对象】

1. 超级块super_block:

 struct super_block {
    struct list_head    s_list;        /* Keep this first 指向超级块链表的指针*/
    dev_t         s_dev;        /*具体文件系统的块设备描述符*/
    unsigned char s_blocksize_bits;
    unsigned long s_blocksize; /*以字节为单位的数据块的大小*/
    loff_t        s_maxbytes;    /* Max file size */
    struct file_system_type    *s_type; /*文件系统类型,每个文件系统只有一个结构体*/
    const struct super_operations    *s_op; /*指向super_block操作的函数集合*/
	alloc_inode () /*创建并初始化一个inode*/
	write_inode() /*将inode同步到磁盘*/
    sync_fs() /*同步文件系统元数据到磁盘*/      
    list_head s_inodes;  /* all inodes */
    list_head  s_inodess_dirty;  /* dirty inodes */
    void *s_fs_info;    /* Filesystem private info 具体文件系统的私有数据*/
*** }
  • 超级块用来描述整个文件系统的信息
  • 每个具体的文件系统都有自己的超级块
  • VFS超级块是各种文件系统在安装时建立的,并在卸载时被自动删除,其数据结构是super_block
  • 所有超级块对象都以双向循环链表的形式链接在一起

1.1 struct super_operations:超级块操作表

2. inode:index node

struct inode {
        struct hlist_node       i_hash;              /* 哈希表 */
        struct list_head        i_list;              /* 索引节点链表 */
        struct list_head        i_dentry;            /* 目录项链表 */
        unsigned long           i_ino;               /* 节点号 */
        atomic_t                i_count;             /* 引用记数 */
        umode_t                 i_mode;              /* 访问权限控制 */
        unsigned int            i_nlink;             /* 硬链接数 */
        uid_t                   i_uid;               /* 使用者id */
        gid_t                   i_gid;               /* 使用者id组 */
        kdev_t                  i_rdev;              /* 实设备标识符 */
        loff_t                  i_size;              /* 以字节为单位的文件大小 */
        struct timespec         i_atime;             /* 最后访问时间 */
        struct timespec         i_mtime;             /* 最后修改(modify)时间 */
        struct timespec         i_ctime;             /* 最后改变(change)时间 */
        unsigned int            i_blkbits;           /* 以位为单位的块大小 */
        unsigned long           i_blksize;           /* 以字节为单位的块大小 */
        unsigned long           i_version;           /* 版本号 */
        unsigned long           i_blocks;            /* 文件的块数 */
        unsigned short          i_bytes;             /* 使用的字节数 */
        spinlock_t              i_lock;              /* 自旋锁 */
        struct rw_semaphore     i_alloc_sem;         /* 索引节点信号量 */
        struct inode_operations *i_op;               /* 索引节点操作表 */
        struct file_operations  *i_fop;              /* 默认的索引节点操作 */
        struct super_block      *i_sb;               /* 相关的超级块 */
        struct file_lock        *i_flock;            /* 文件锁链表 */

        //important
        struct address_space    *i_mapping;          /* 描述页高速缓存中的页面。一个文件对应        
                                                        一个address_space,一个 
                                                        address_space和一个偏移量可以确定一 
                                                       个页高速缓存中的页面
        struct address_space    i_data;              /* 设备地址映射 */

        struct dquot            *i_dquot[MAXQUOTAS]; /* 节点的磁盘限额 */
        struct list_head        i_devices;           /* 块设备链表 */
        struct pipe_inode_info  *i_pipe;             /* 管道信息 */
        struct block_device     *i_bdev;             /* 块设备驱动 */
        unsigned long           i_dnotify_mask;      /* 目录通知掩码 */
        struct dnotify_struct   *i_dnotify;          /* 目录通知 */
        unsigned long           i_state;             /* 状态标志 */
        unsigned long           dirtied_when;        /* 首次修改时间 */
        unsigned int            i_flags;             /* 文件系统标志 */
        unsigned char           i_sock;              /* 可能是个套接字吧 */
        atomic_t                i_writecount;        /* 写者记数 */
        void                    *i_security;         /* 安全模块 */
        __u32                   i_generation;        /* 索引节点版本号 */
        union {
                void            *generic_ip;         /* 文件特殊信息 */
        } u;
  • 文件系统处理文件所需要的所有信息都保存在称为索引节点的inode结构体中
  • 同一个文件系统中,每个文件的索引节点号都是唯一的
  • 与索引节点关联的方法由struct inode_operations来描述
  • inode有两个设备号:i_dev(常规文件的设备号),i_rdev(某一设备的设备号)
  • LInux文件系统的另外一大特色:设备即文件 —— 驱动中设备号的来源。

2.1 struct inode_operations:index node操作函数

 

3. dentry:目录项

struct dentry {
    atomic_t d_count;                     //目录项对象使用计数器,可以有未使用态,使用态和负状态 
    unsigned int d_flags;                  //目录项标志 
    struct inode *d_inode;                 //与文件名关联的索引节点 
    struct dentry *d_parent;               //父目录的目录项对象 
    struct list_head d_hash;               //散列表表项的指针 
    struct list_head d_lru;                 //未使用链表的指针 
    struct list_head d_child;               //父目录中目录项对象的链表的指针 
    struct list_head d_subdirs;             //对目录而言,表示子目录目录项对象的链表 
    struct list_head d_alias;               //相关索引节点(别名)的链表 
    int d_mounted;                          //对于安装点而言,表示被安装文件系统根项 
    struct qstr d_name;                     //文件名 
    unsigned long d_time;         /* used by d_revalidate */ 
    struct dentry_operations *d_op;         //目录项方法 
    struct super_block *d_sb;               //文件的超级块对象 
    vunsigned long d_vfs_flags; 
    void *d_fsdata;                         //与文件系统相关的数据 
    unsigned char d_iname [DNAME_INLINE_LEN]; //存放短文件名
};
  • 存在于内存的目录项缓存,为了提高查找性能而设计,动态生成的(哈希表,或组织为一颗树,链表)。
  • 每个文件除了一个struct inode结构体外,还要一个目录项struct dentry结构,通过其d_hash域链入哈希表中
  • 不管是文件夹还是文件(目录),都属于目录项,所有的目录项在一起构成一颗庞大的目录树。
  • dentry对象有三种状态:被使用,未被使用和负状态。
•dentry 建立流程:
某个目录对应的dentry不在内存中,调用d_lookup函数,以父dentry和qstr类型的name为依据,来查找内存中是否已经有了对应的dentry。如无,分配一个dentry,这是d_alloc函数负责分配dentry结构体,初始化相应的变量,建立与父dentry的关系。

3.1 struct dentry_operations

4. 文件对象(file)

  • 进程通过文件描述符来访问文件,由相应的open()系统调用创建, 有close()系统调用销毁。
  • LInux用一个file文件对象来保存打开文件的位置,这个对象称为打开的文件描述符。
  • file结构主要保存了文件位置,还把指向文件索引节点的指针也放在其中。
  • file结构形成一个双链表,称为系统打开文件表。
  • 只有目录项对象才表示已打开的实际文件,文件对象实际上没有对应的磁盘数据。

struct file { 
  union { struct list_head fu_list; //文件对象链表指针linux/include/linux/list.h struct         
    rcu_head fu_rcuhead; //RCU(Read-Copy Update)是Linux 2.6内核中新的锁机制 }
    f_u; struct path f_path; //包含dentry和mnt两个成员,用于确定文件路径
 
#define f_dentry f_path.dentry //f_path的成员之一,当前文件的dentry结构 
#define f_vfsmnt f_path.mnt //表示当前文件所在文件系统的挂载根目录 
const struct file_operations *f_op; //与该文件相关联的操作函数 
atomic_t f_count; //文件的引用计数(有多少进程打开该文件) 
unsigned int f_flags; //对应于open时指定的
flag mode_t f_mode; //读写模式:open的mod_t mode参数 
off_t f_pos; //该文件在当前进程中的文件偏移量 
struct fown_struct f_owner; //该结构的作用是通过信号进行I/O时间通知的数据。 
unsigned int f_uid, f_gid; //文件所有者id,所有者组id 
struct file_ra_state f_ra; //在linux/include/linux/fs.h中定义,文件预读相关 
unsigned long 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; 文件的事件轮询等待者链表的头, 
spinlock_t f_ep_lock; 
f_ep_lock是保护f_ep_links链表的自旋锁。 
#endif /* #ifdef CONFIG_EPOLL */ 
struct address_space *f_mapping; 文件地址空间的指针
};

4.1 struct file_operations

 4.2  struct files_struct


struct files_struct { 
  atomic_t count;         /* 共享该表的进程数 */ 
  rwlock_t file_lock;     /* 保护该结构体的锁*/ 
  int max_fds;          /*当前文件对象的最大数*/
  int max_fdset;         /*当前文件描述符的最大数*/ 
  int next_fd;           /*已分配的文件描述符加1*/
  struct file ** fd;     /* 指向文件对象指针数组的指针 */ 
  fd_set *close_on_exec;        /*指向执行exec()时需要关闭的文件描述符*/
  fd_set *open_fds;          /*指向打开文件描述符的指针*/ 
  fd_set close_on_exec_init;      /* 执行exec()时关闭的初始文件*/ 
  fd_set open_fds_init;           /*文件描述符的初值集合*/ 
  struct file * fd_array[32];     /* 文件对象指针的初始化数组*/
};
  • 文件描述符用来描述打开的文件
  • 每个进程用一个files_struct结构来记录文件描述符的使用情况
  • 这个files_stuct结构称为用户打开文件表,它是进程的私有数据

  4.3  struct fs_struct

//描述进程与文件系统的关系
struct fs_struct {
   int users;
   spinlock_t lock;
   seqcount_t seq;
   int umask;  //用于为新创建的文件设置初始文件许可权限
   int in_exec;
   struct path root, pwd;
};

关系图:

 贪婪和成本效益算法

PS:

【I/O 缓冲区】

Cache:解决的是速度不同步的问题。

  • Buffer : 用于内存和硬盘的缓冲,缓冲“写”操作,保存即将要写入到磁盘上的数据。
  • Cache:一般指高速缓存,用于CPU和内存之间的缓冲,解决读的问题,保存从磁盘上读出的数据。

Buffer Cache和 Page Cache

  • buffer cache:块缓冲器,面向块设备(文件系统的块)。
  • page cache:  页缓冲器,面向虚拟内存。已映射到内存的某些物理设备(例如磁盘)上的数据,包含来自最近访问的“文件”的整个页面。在页面I / O操作(例如read()])中,内核检查数据是否驻留在page cache中。如果数据在page cache中,则内核可以快速返回请求的页面,而不必从磁盘读取数据

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值