文件系统---文件系统的基本概念

来源:互联网 发布:java 分布式事务 编辑:程序博客网 时间:2024/06/06 19:58

文件系统

        Linux内核的应用层是以 文件系统 为核心展开,以文件系统作为整个内核应用层的核心,理由如下:

--文件系统本身具有重大作用

    分布式文件系统的广泛应用让文件系统成为当前内核应用的热门

--文件系统在整个内核架构中具有基础架构性质

    字符设备、块设备 这些设备驱动 需要依靠文件系统来实现。设备管理的基础架构也要依靠文件系统(sysfs)。设备和驱动 是国内当前在内核层面应用,也是国内底层开发中应用最多的方面。


        从文件系统入手,掌握基本概念实现架构后,可以从稳健系统引出设备文件的概念,设备文件可以引申到 字符设备 和 块设备,这样从 文件系统 过度到 设备管理。

        设备管理包含 设备驱动, 设备驱动要用到中断,设备里面的块设备又控制了 通用块层 和 I/O调度。 

        而文件系统向外引申又和网络的socket联系。

        深入文件系统的的代码,可以了解到内存的页面管理。

        从文件系统出发,层次推进基本囊括了内核的应用层的重要概念和架构。


2.1 文件系统的基本概念

        

2.1.1 VFS

        VFS------ VIrtual File System 虚拟文件管理系统

        linux内核通过虚拟文件系统(VFS)管理文件系统;

        VFS是linux内核文件系统的一个极其重要的基础设施,VFS为 所有的文件系统提供统一的接口,对每个文件系统的访问都需要通过VFS定义的接口来实现。同时,VFS也是一个极其重要的架构,所有的linux文件系统都必须按照VFS定义的方式来实现;

VFS存在于内存中,将硬盘上的文件系统抽象到内存中。

VFS定义了几个重要结构:

dentryinodesuper_block,通过这些结构将真实的硬盘文件系统抽象到内存。

通过管理dentry、inode、super_block这几个对象就可以完成对文件系统的一些操作-----合适的时候仍需要将内存数据写入到硬盘


2.1.2 超级块super_block

        超级块suoer_block代表整个文件系统本身。

超级块是对应文件系统本身的控制块结构【可参考ext2文件系统的超级块结构】。

超级块保存了文件系统设定的文件块大小超级块的操作函数文件系统内所有的inode也要链接到超级块的表头

对于一个具体文件系统的控制块可能还含有另外的信息,通过超级块对象,可以找到这些必要的信息。

超级快的内容需要读取具体文件系统在硬盘上的超级块获得,所以超级块是具体文本系统超级块的内存抽象

超级块对象整个结构很庞大庞杂,以下是超级块的简化后的定义:

---------------------------------------------------------------------------------------struct super_block{unsigned longs_blocksize;unsigned chars_blocksize_bit;......unsigned long longs_maxbytes;struct file_system_type*s_type;struct super_operations*s_op;unsigned longs_magic;struct dentry*s_root;struct list_heads_inodes;struct list_heads_dirty;struct block_device*s_bdev;void*s_fs_info;}

从两方面了解超级结构快的作用:

1)超级块结构给出了文件系统的全局信息

*s_blocksize ---指定了文件系统的大小

*s_maxbytes ---指定文件系统中最大文件的尺寸

*s_type          ---指向file_system_type结构的指针

*s_magic      ---魔术数字,每个文件系统都有一个魔术数字

*s_root ---指向文件 系统根dentry的指针


超级块还定义了一些链表头:

*s_inode ---指向文件系统内所有的iNode,通过他可以遍历inode对象

*s_dirty   ---指向所有dirty的inode对象

*s_block ---指向文件系统存在的块设备指针


2)超级块结构包含一些函数的指针

-------super_operation提供了最重要的超级块操作

例如super_operation的成员函数read_inode提供读取inode信息的功能。

每个具体的文件系统需要提供这个函数来实现对inode信息的读取,例如ext2文件系统--具体函数是ext2_read_inode

(我们可以理解为‘VFS提供了架构,具体文件系统按照VFS的架构实现’)



目录项dentry

----文件和目录一般按照树状结构保存。目录项【dentry】就反应了文件系统的这种树状关系。

---在VFS中,目录本身就是一个文件;

---------------------------------------------------------------------------

struct  dentry {

..../省略dentry锁、标志等代码/

struct inode *d_inode;

struct hlist_node d_hash;

struct dentry *d_parent;

struct qstr d_name;


/* d_child and d_rcu can share memory*/

union {

  struct list_head_d d_child;

  struct rcu_head d_rcu;

} d_u;

struct list_headd_subdirs;


struct dentry_operations*d_op;

struct super_block*d_sb;

int d_mounted;

}

---------------------------------------------------------------------------

对dentry的解释如下:



索引节点inode

---inode代表一个文件。inode保存了一个文件的大小、创建时间、文件的块大小等参数,以及文件的读写函数、文件的读写缓存等信息。

---文件只有一个inode,可以有多个dentry【指向文件的路径可以多个(考虑文件的链接)】。

inode和dentry分别代表文件通用的两个部分


inode结构定义十分庞大,简化重点的几个结构成员如下:

--------------------------------------------------------------------------------------

struct inode {

struct list_head i_list;

struct list_head i_sb_list;

struct list_head i_dentry;

unsigned long i_ino;

atomic_t i_count;

loff_t i_size;


unsigned int i_blkbits;

struct inode_operations *i_op;

const struct file_operation *i_fop;

struct address_space *i_mapping;

struct block_device *i_bdev;

......

};

--------------------------------------------------------------------------------------

inode 结构解释:

--i_list 链接描述inode当前状态的链表

--i_sb_list 链接到超级块中的inode链表

***当创建一个新的inode的时候,成员i_list链接到inode_in_use这个链表【表示inode在使用状态】,同时成员i_sb_list链接到文件系统超级块的s_inode链表头。

--i_dentry 一个文件有多个dentry,这些dentry都连接到成员i_dentry这个链表头

------i_ino inode的号

------i_count inode的引用计数

------i_size 以字节为单位的文件长度

-i_blkbits 文件块的位数


文件

文件对象的作用是描述 进程 和 文件 交互 的 关系;

硬盘上并不存在一个文件结构

进程打开一个文件,内核就动态创建一个文件对象

同一个文件,在不同进程中有不同的文件对象

文件的结构定义如下代码清单:

-------------------------------------------------------------------------

struct file {

  struct dentry *f_dentry;

  struct vfsmount *f_vfsmnt;

  const struct file_operation *f_op;

.......

  loff_t f_ops;

  struct fown_struct f_owner;

  usigned int f_uid,

  struct file_ra_state f_ra;

  

  struct address_space *f_mapping;

}

-------------------------------------------------------------------------


--f_ra 用于文件预读的设置---在第10章继续分析文件



注释:

文件预读:

--对于文件的预读,Linux内核提供了预读策略------比要求读的长度多读一些,存储在page cache里,后续读如果是顺序的,马上可以利用page cache的数据返回,不必再次读硬盘。对于硬盘这种慢速设备来说,利用缓存数据可以大大提升I/O传输效率。

内核提供默认的预读参数,代码清单如下:

--------------------------------------------------------------------------------------------------------------

struct  backing_dev_info  default_backing_dev_info = {

.ra_page=(VM_MAX_READAHEAD * 1024)  /  PAGE_CACHE_SIZE,

.state=0,

.capabilities=BDI_CAP_MAP_COPY,

.unplug_io_fn=default_unplug_io_fn,

}

--------------------------------------------------------------------------------------------------------------

linux-2.6.39\mm\backing-dev.c

struct backing_dev_info default_backing_dev_info = {.name= "default",.ra_pages= VM_MAX_READAHEAD * 1024 / PAGE_CACHE_SIZE,.state= 0,.capabilities= BDI_CAP_MAP_COPY,};

VM_MAX_READAHEAD 默认设置为128KB,即默认读取页面是32个4KB的页面。

Linux内核会根据文件读取是否顺序启动预读参数和设置预读窗口,对于连续的顺序读,会尽量多读一些内容填充page cache。

注--这部分的内容在readahead,c文件里面。文件不大,比较孤立,不涉及太多关联的知识点,可以分析一下预读代码