文件系统理论详解,Linux操作系统原理与应用

目录

一、Linux 文件系统基础

        1、Linux 文件结构

        2、文件类型

        3、存取权限与文件模式

        4、Linux 文件系统

        (1)索引节点

        (2)软链接和硬链接

        (3)安装文件系统

二、虚拟文件系统

        1、虚拟文件系统的引入

        2、VFS中的对象演绎

        3、VFS的超级块

       (1)什么是超级块

        (3)超级块数据结构

        4、VFS的索引节点

        5、目录项对象

        6、与进程相关的文件结构

        (1)文件对象

        (2)用户打开文件表

        (3)fs_struct 结构

        7、主要数据结构间的关系

        8、实例——观察数据结构中的数据

三、文件系统的注册、安装与卸载

        1、文件系统的注册和注销

        2、文件系统的安装

        3、文件系统的卸载

四、文件的打开与读写

        1、文件打开

        2、文件读写

五、编写一个文件系统

        1、Linux 文件系统的实现要素

        2、Romfs 文件系统

        3、Romfs 文件系统布局与文件结构


一、Linux 文件系统基础

        从系统角度来看,文件系统是对文件存储器空间进行组织和分配,负责文件的存储并对存入的文件进行保护和检索的系统。具体地说,它负责为用户建立文件,存入、读出、修改、转储文件,控制文件的存取,等对应文件的一系列操作。一句话来讲,文件系统就是文件的管理者和存储的文件的物理空间。

        1、Linux 文件结构

        文件结构是文件存放在磁盘等存储设备上的组织方式。主要体现在对文件和目录的组织上,主要目的是方便查找,实现按名存取。

        Linux使用标准的目录结构-树型结构,无论操作系统管理几个磁盘分区,这样的目录树只有一个。在 Linux 安装时,安装程序就已经为用户创建了文件系统和完整而固定的目录组成形式,并指定了每个目录的作用和其中的文件类型,如图:

        为什么不能有多个根目录和目录树?

        因为 Linux 是一个多用户系统,制定这样一个固定的目录规划有助于对系统文件和不同的用户文件进行统一管理。 

        2、文件类型

        (1)常规文件

        计算机用户和操作系统用于存放数据、程序等信息的文件。一般都长期地存放在外存储器中。常规文件一般分为文本文件和二进制文件。

        (2)目录文件

        将文件索引节点号文件名结合在一起的一张表。目录文件只允许系统进行修改,用户进程可以读取目录文件,但不能对它们进行修改。

        (3)设备文件

        Linux 把所有的外设都当作文件来看待。每一种 I/O 设备对应一个设备文件,存放在 /dev 目录中。

        (4)管道文件

        主要用于在进程间传递数据。某进程数据写入管道的一端,另一个进程从管道另一端读取数据。管道又称为先进先出文件。

        (5)链接文件

        链接文件又称为符号链接文件,它提供了共享文件的一种方法。在链接文件中不是通过文件名实现文件共享的,而是通过链接文件中包含的指向文件的指针(软链接)来实现对文件的访问。

        3、存取权限与文件模式

        为了保证文件信息的安全,Linux 设置了文件保护机制,其中之一就是给文件都设定了一定的访问权限。Linux 对文件的访问设定了三级权限:文件所有者、与文件所有者同组的用户和其他用户。对文件的访问主要是三种处理操作:读取、写入和执行。因此,形成了 9 种情况。对于如何修改文件权限这里不展开叙述(在我的另外一篇博客里有)。

        4、Linux 文件系统

        文件系统指文件存在的物理空间,Linux 系统中每个分区都是一个文件系统,都有自己的目录层次结构。Linux 会将这些分属不同分区的、单独的文件系统按一定的方式形成一个系统的总的目录层次结构,即目录树。

        (1)索引节点

        Linux 文件系统使用索引节点来记录文件信息。索引节点是一个数据结构,它包含文件的长度、创建时间、权限、所属关系、磁盘中的位置等信息。系统给每个索引节点分配了一个号码,称为索引节点号。文件系统正是靠这个索引节点号来识别一个文件。

        (2)软链接和硬链接

        可以用链接命令ln(Link)对一个已存在的文件再建立一个新的链接,而不复制文件的内容。链接有软链接和硬链接两种。

        硬链接就是让一个文件对应一个或多个文件名,或者说把我们使用的文件名和文件系统使用的节点号链接起来,这些文件名可以在同一目录或不同目录。一个文件有几个硬链接,就有几个索引节点(或者文件名)。硬链接有两个限制:一是不允许给目录创建硬链接(因为没有必要,目录只是一个结构,不能执行),二是只有在同一文件系统中的文件之间才能创建链接(因为索引节点存放在磁盘中,而一个磁盘分区对应一个文件系统)。

        软链接又称为符号链接,是一种特殊的文件,这种文件包含了另一个文件的任意一个路径名。这个路径名指向位于任意一个文件系统的任意文件,甚至可以指向一个不存在的文件。

        可以对一个已有的文件 a.c 操作如下:

$ ln a.c hard.c    // 创建一个硬链接为 hard.c

$ ln -s a.c symbolic.c    // 创建一个软链接为 symbolic.c

        (3)安装文件系统

        可以将一个文件系统的顶层目录挂到另一个文件系统的子目录上,使它们成为一个整体,称为“安装(Mount)”。安装一个文件系统用 mount 命令,如下:

$ mkdir /mnt/point    // 创建安装点
$ mount -t ext2 /dev/loop /mnt/point    // 在安装点上安装 Ext2 文件系统

        由于 Ext2 / Ext3 是 Linux 的标准文件系统,所以系统把 Ext2 文件系统的磁盘分区作为系统的根文件系统,其它的文件系统则安装在根文件系统的某个目录下,成为系统树状结构中的一个分支。

二、虚拟文件系统

        为了保证 Linux 的开放性,让 Linux 除支持 Ext2 文件系统外,还能支持其他各种不同的文件系统。因此,就必须将各种不同的文件系统的操作和管理纳入到一个统一的框架中,使得用户程序可以通过同一个文件系统界面,能够对各种不同的的文件系统以及文件进行操作。这种统一的框架就是所谓的虚拟文件系统转换(Virtual Filesystem Switch),一般简称为虚拟文件系统(VFS)。

        1、虚拟文件系统的引入

        Linux 最初采用的是 MINIX 的文件系统,但是其大小限于64MB,文件名长度也限于14个字节。所以Linux经过一段时间的改进和发展,特别是吸取了Unix文件系统的经验,最后形成了Ext2文件系统。

        虚拟文件系统所提供的抽象界面主要由一组标准的、抽象的操作构成,例如 read()、write()、lseek() 等,这些函数以系统调用的形式供用户程序调用。

        

        2、VFS中的对象演绎

        虚拟文件系统在磁盘中并没有对应的存储的信息。尽管 Linux 支持多达几十种文件系统,但这些真实的文件系统并不是一下子都挂在系统中的,它们实际上是按需挂载的。另外,这些实的文件系统只有安装到系统中,VFS 才予以认可,也就是说,VFS 只管理挂载到系统中的实际文件系统。

        路径中的每一个部分被称作目录项,例如 /home/clj/myfile 中,根目录是 / ,而 home,clj 和文件 myfile 都是目录项。

        VFS 有 4 个主要对象:

        (1)超级块对象:存放系统中已安装文件系统的有关信息。

        (2)索引节点对象:存放关于具体文件的一般信息。

        (3)目录项对象:存放目录项与对应文件进行链接的信息。

        (4)文件对象:存放打开文件与进程之间进行交互的有关信息。

        3、VFS的超级块

       (1)什么是超级块

        超级块用来描述整个文件系统的信息。对于每个具体的文件系统来说,都有各自的超级块,如 Ext2 超级块和 Ext3 超级块,它存放在磁盘上。

        (2)何时何地拥有一个VFS超级块

        当内核在对一个文件系统进行初始化和注册时在内存为其分配一个超级块。此时的超级块为VFS 超级块。也就是说,VFS 超级块是各种具体文件系统在安装时建立的,并在这些文件系统卸载时被自动删除。VFS 超级块只存放在内存中。

        (3)超级块数据结构

struct super_block
{
    dev_t s_dev;    // 
    unsigned long s_blocksize;    // 以字节为单位数据块的大小
    unsigned char s_blocksize_bits;    // 块大小的值所占用的位数,

    ...
    
    struct list_head s_list;    // 指向超级块链表的指针

    struct file_system_type *s_type;    // 指向文件系统的 file_system_type 的指针

    struct super_operation *s_op;    // 指向具体文件系统的用于超级块操作的函数集合

    struct mutex s_lock;

    struct list_head s_dirty;

    ...

    void *s_fs_info;    // 指向具体文件系统的超级块
};

        从上面定义的数据结构可知:所有的超级块对象都以双向循环链表的形式链接在一起。链表中第一个元素用 super_blocks 变量来表示。

        与超级块关联的方法就是所谓的超级块操作表,其数据结构是 super_operations,定义如下:

struct super_operations
{
    void (*write_super) (struct super_block *);    // 将超级块的信息写回磁盘

    void (*put_super) (struct super_block *);    // 释放超级块对象

    void (*read_inode) (struct inode *);    // 读取某个文件系统的索引节点

    void (*write_inode) (struct inode *, int);    // 把索引节点写回磁盘

    void (*put_inode) (struct inode *);    // 逻辑上释放索引节点

    void (*delete_inode) (struct inode *);    // 从磁盘上删除索引节点

};

        4、VFS的索引节点

        文件系统处理文件所需要的所有信息都存放在索引节点中。在同一个文件系统中,每个索引节点号都是唯一的 。

        具体文件系统的索引节点是存放在磁盘上,是一种静态结构,要使用它,必须调入内存,填写 VFS 的索引节点,因此,也称 VFS 索引节点是动态节点。

        VFS 索引节点数据结构的主要域定义如下:

struct inode
{
    struct list_head    i_hash;    // 指向哈希表的指针
    struct list_head    i_list;    // 指向索引节点链表的指针
    struct list_head    i_dentry;  // 指向目录项链表的指针
    ...
    
    unsigned long i_ino;    // 索引节点号
    umode_t    i_mode;      // 文件的类型与访问权限
    kdev_t     i_rdev;      // 实际设备标识号
    uid_t    i_uid;         // 文件拥有者标识号
    gid_t    i_gid;         // 文件拥有者所在组的标识号
    ...
    struct inode_operations *i_op;    // 指向对该节点进行操作的一组函数

    struct super_block     *i_sb;     // 指向该文件系统超级块的指针

    atomic_t    i_count;    // 当前使用该节点的进程数,计数为0时,表明该节点可丢弃或重新使用

    struct file_operations *i_fop;    // 指向文件操作的指针
    ...
    struct vm_area_struct *i_op;      // 指向对文件进行映射所使用的虚存区指针

    unsigned long    i_state;    // 索引节点的状态标志
    unsigned int     i_flags;    // 文件系统的安装标志
    
    union    // 联合结构体,其成员指向具体文件系统的 inode 结构
    {
        struct minix_inode_info     minix_i;
        struct Ext2_inode_info      Ext2_i;
    }
};

       在同一个文件系统中,每个索引节点号都是唯一的 ,内核可以根据索引节点号的哈希值查找其 inode 结构,前提是内核要知道索引节点号和对应文件所在文件系统的超级块对象的地址。

        与索引节点关联的方法叫索引节点操作表,由 inode_operations 结构来描述:

struct inode_operations
{
    // 创建一个新的磁盘索引节点
    int (*create) (struct inode *, struct dentry *, int);
    
    // 查找一个索引节点所在的目录
    struct dentry * (*lookup) (struct inode *, struct dentry *);
    
    // 创建一个新的硬链接
    int (*link) (struct dentry  *, struct inode *, struct dentry *);

    // 删除一个硬链接
    int (*unlink) (struct inode *, struct dentry *);
    
    // 为符号链接创建一个新的索引节点
    int (*symlink) (struct inode *, struct dentry *, const char *);
    
    // 为目录项创建一个新的索引节点
    int (*mkdir) (struct inode *, struct dentry *, int);

    // 删除一个目录项的索引节点
    int (*rmdir) (struct inode *, struct dentry *);
};

         对于不同的文件系统,其每个函数的具体实现是不同的,也不是每个函数都必须实现,没有实现的函数对应的域当置为 NULL。

        5、目录项对象

        每个文件除了有一个索引节点inode数据结构外,还有一个目录项dentry数据结构。 

        dentry结构代表的是逻辑意义上的文件,描述的是文件逻辑上的属性,目录项对象在磁盘上并没有对应的映像。

        inode结构代表的是物理意义上的文件,记录的是物理上的属性,对于一个具体的文件系统,其inode结构在磁盘上就有对应的映像。

        一个索引节点对象可能对应多个目录项对象(因为路径的每一部分称作目录项,而文件的路径很长)。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;      // 未使用的 LRU 链表
    struct list_head d_child;    // 父目录的子目录项所形成的链表
    struct list_head d_subdirs;  // 该目录项的子目录所形成的的链表
    struct list_head d_alias;    // 索引节点别名的链表
    
    int d_mounted;               // 目录项的安装点
    struct qstr d_name;          // 目录项名(可快速查找)
    struct dentry_operations *d_op;    // 操作目录项的函数
    struct super_block *d_sb;    // 目录项树的根
    unsigned long d_vfs_flags;
    void *d_fsdata;              // 具体文件系统的数据
    unsigned char d_iname[DNAME_INLINE_LEN];    // 短文件名
    ...

};

        一个有效的 dentry 结构必定有一个 inode 结构,然后一个 inode 却可能对应着不止一个 dentry 结构,也就是说,一个文件名可以有不止一个文件名或路径名。

        对目录项进行操作的一组函数叫目录项操作表,由 dentry_operations 结构描述:

struct dentry_operations
{
    // 判定目录项是否有效
    int (*d_revalidate) (struct dentry *, int);
    
    // 生成一个哈希值
    int (*d_hash) (struct dentry *, struct qstr *);
    
    // 比较两个文件名
    int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
    
    // 删除d_count域为 0 的目录项对象
    int (*d_delete) (struct dentry *);
    
    // 释放一个目录项对象
    void (*d_release) (struct dentry *);

    // 调用该方法丢弃目录项对应的索引节点
    void (*d_iput) (struct dentry *, struct inode *);
};

        6、与进程相关的文件结构

        (1)文件对象

        Linux中专门用了一个file文件对象来保存打开文件的文件位置,这个对象称为打开的文件描述(open file description)。这样做是为了解决文件共享的问题,所以不把文件位置存放在索引节点。其中,file 结构形成了一个双链表,称为系统打开文件表。

  file 结构主要域如下:

struct file
{
    struct list_head        f_list;      // 所有打开的文件形成一个链表
    struct dentry           *f_dentry;   // 与文件相关的目录项对象
    struct vfsmount         *f_mount;    // 该文件所在的已安装文件系统
    struct file_operations  *f_op;       // 指向文件操作表的指针
    mode_t     f_mode;                   // 文件的打开模式
    loff_t     f_pos;                    // 文件的当前位置
    unsigned short f_flags;              // 打开文件时所指定的标志
    unsigned short f_count;              // 使用该结构的进程数
    
    ...
};

        对文件进行操作的一组函数叫文件操作表,由 file_operations 结构描述,如下:

struct file_operations
{
    // 修改文件指针
    loff_t (*llseek) (struct file *, loff_t, int);

    // 从文件中读取若干个字节
    ssize_t (*read)  (struct file *, char *, size_t, loff_t *);

    // 给文件中写若干个字节
    ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
    
    // 文件到内存的映射
    int (*mmap) (struct file *, struct vm_area_struct *);

    // 打开文件
    int (*open) (struct inode *, struct file *);

    // 关闭文件时减少 f_count 计数
    int (*flush) (struct file *);
    
    // 释放 file 对象
    int (*release) (struct inode *, struct file *);

    // 文件在缓冲区的数据写回磁盘
    int (*fsync) (struct file *, struct dentry *, int datasync);

    ...
};

        (2)用户打开文件表

        文件描述符是用来描述打开的文件的。每一个进程用一个 files_struct 结构来记录文件描述符的使用情况,即一个进程可以有多个文件描述符,因为一个进程可以打开多个文件。而通过 dup()、dup2() 和 fcntl() 两个文件描述符可以指向同一个打开的文件,数组的两个元素可能指向同一个文件对象。

        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];    // 文件对象指针的初始化数组
};

        (3)fs_struct 结构

        fs_struct 结构描述进程与文件系统的关系,其定义为;

struct fs_struct
{
    atomic_t count;    // 表示共享同一 fs_struct 表进程数目
    rwlock_t lock;
    int umask;    // 为新创建的文件设置初始文件许可权
    struct dentry *root, *pwd, *altroot;    // 对目录项的描述
    struct vfsmount *rootmnt, *pwdmnt, *altrootmnt;    // 目录安装点的描述
};

        7、主要数据结构间的关系

        超级块是对一个文件系统的描述;索引节点是对一个文件物理属性的描述;而目录项是对一个文件逻辑属性的描述。

        一个进程所处的位置是由 fs_strcut 来描述的,而一个进程(或者用户)打开的文件是由 files_struct 来描述的,而整个系统所打开的文件是由 file 结构来描述的。

        8、实例——观察数据结构中的数据

        编写一个内核模块,打印 super_block 结构中一些域的值。(这里不展开,具体内容在我的另外一篇博客里有)

三、文件系统的注册、安装与卸载

        1、文件系统的注册和注销

        当内核被编译时,就已经确定了可以支持哪些文件系统,这些文件系统在系统引导时,在 VFS 中进行注册。如果文件系统是作为内核可装载的模块,则在实际安装时进行注册,并在模块卸载时注销。

        每一个文件系统都有一个初始化例程,它的作用就是在 VFS 中进行注册,即填写一个叫做 file_system_type 的数据结构。所有已注册的文件系统的 fiile_system_type 结构形成一个链表,称为注册链表。

        

         file_system_type 的数据结构定义如下:

struct file_system_type
{
    const char *name;    // 文件系统的名称
    int fs_flags;        // 文件系统的一些特性

    // 文件系统读入其超级块的函数指针
    struct super_block * (*read_super) ( struct super_block *, void *, int);

    // 通常置为宏 THIS_MODULE,用于确定是否把文件系统作为模块来安装
    struct module *owenr;  
    struct file_system_type *next;
};

        要对一个文件系统进行注册,可以调用 register_filesystem() 函数。如果想进行注销操作,可以 调用 unregister_filesystem() 函数。

        2、文件系统的安装

        文件系统可以在操作系统启动时安装,也可以在操作系统启动后,通过挂载内核模块的方式进行安装。安装一个文件系统需要指定三种信息:文件系统的名称包含文件系统的物理块设备,文件系统在已有文件系统中安装点。可以说安装一个文件系统其实就是安装一个块设备

        安装点的数据结构为 vfsmount,定义如下:

struct vfsmount
{
    struct list_head mnt_hash;        // 哈希表
    struct vfsmount *mnt_parent;      // 指向上一层安装点的指针
    struct dentry *mnt_mountpoint;    // 安装点的目录项
    struct dentry *mnt_root;          // 安装树的根
    struct super_block *mnt_sb;       // 指向超级块的指针
    struct list_head mnt_mounts;      // 子链表
    struct list_head mnt_child;       // 通过 mnt_child 进行遍历
    atomic_t mnt_count;
    int mnt_flags;
    char *mnt_devname;                // 设备名
    struct lsit_head mnt_list;
};

        每一个文件系统都有自己的根目录,如果某个文件系统的根目录是系统目录树的根目录,那么该文件系统称为根文件系统。而其他的文件系统可以安装在系统的目录树上,把这些文件系统要插入的目录就称为安装点

        3、文件系统的卸载

        如果文件系统中文件当前正在使用,该文件系统是不能被卸载的。跟打开文件,然后想删除它却不能执行的原因是一样的。可以通过 VFS索引节点的使用计数来查看某个文件是否正在使用。

        否则,即没有文件正在使用,就去查看对应的 VFS 超级块。如果该文件系统的 VFS 超级块标志为“脏”,则必须将超级块信息写回磁盘,然后才将 VFS 超级块释放。

四、文件的打开与读写

        对文件的常见操作是三个系统调用 open()、read() 和 write() 。

        1、文件打开

        open() 系统调用就是打开文件。所谓打开文件实质上是在进程与文件之间建立一种连接,用文件描述符来标识这个连接。同时,将目标文件的索引节点从磁盘载人内存,并对其初始化,形成 VFS 索引节点,同时产生一个或多个目录项对象。

        具体代码不做分析,在我的另外一篇博客有。

        2、文件读写

        文件读写的基本步骤 :

        (1)file=fget(fd),也就是调用fget( )从fd获取相应文件对象的地址file,并把引用计数器file->f_count加1。

        (2)检查file->f_mode中的标志是否允许所请求的访问(读或写操作)。

        (3)调用locks_verify_area( )检查对要访问的文件部分是否有强制锁。

        (4)调用file->f_op->read 或file->f_op->write来传送数据。这两个函数都返回实际传送的字节数。另一方面的作用是,文件指针被更新。

        (5)调用fput( )以减少引用计数器file->f_count的值。

        (6)返回实际传送的字节数。

        在文件读写的基本步骤中,f_op->read或 f_op->write两个方法属于VFS提供的抽象方法,对于具体的文件系统,必须调用针对该具体文件系统的具体方法

        对基于磁盘的文件系统,比如EXT2等,所调用的具体的读写方法都是Linux内核已提供的通用函数generic_file_read()或generic_file_write()。简单地说,这些通用函数的作用是确定正被访问数据所在物理块的位置,并激活块设备驱动程序开始数据传送,所以基于磁盘的文件系统没必要再实现专用函数了。

        从用户发出读请求到最终的从磁盘读取数据的步骤:

        (1)用户界面层——负责从用户函数经过系统调用进入内核

        (2) 基本文件系统层——负责调用文件读方法,从缓冲区中搜索数据页,返回给用户。

        (3)I/O调度层——负责对请求排队,从而提高吞吐量。

        (4)I/O传输层——利用任务队列,异步操作设备控制器,完成数据传输。

        读操作流程图:

        

 

五、编写一个文件系统

        1、Linux 文件系统的实现要素

        编写新文件系统涉及一些基本对象,具体地说,需要建立“一个结构四个操作表”:

        (1)文件系统类型结构(file_system_type)

        (2)超级块操作表 (super_operations)

        (3)索引节点操作表(inode_operations)

        (4)页缓冲表(address_space_operations)

         必须建立一个文件系统类型(file_system_type)来描述文件系统,它含有文件系统的名称、类型标志以及get_sb()等操作。当安装文件系统时,系统会对该文件系统进行注册,即填充file_system_type结构,然后调用get_sb()函数来建立该文件系统的超级块。注意对于基于块的文件系统,如ext2、romfs等,需要从文件系统的宿主设备读入超级块,然后在内存中建立对应的超级块,如果是虚文件系统(如proc文件系统),则不读取宿主设备的信息(因为它没有宿主设备),而是在现场创建一个超级块,这项任务也由get_sb()完成。

        超级块是一切文件操作的鼻祖,因为超级块是我们寻找索引节点的唯一源头。操作文件必然需要获得其对应的索引节点(或从宿主设备读取或现场建立),而获取索引节点是通过超级块操作表提供的read_inode()函数完成的。同样操作索引节点的底层次任务,如创建一个索引节点、释放一个索引节点,也都是通过超级块操作表提供的有关函数完成的。所以超级块操作表(super_operations)是第二个需要创建的数据结构。

        除了派生或释放索引节点等操作是由超级块操作表中的函数完成外,索引节点还需要许多自操作函数,比如lookup()搜索索引节点,建立符号链接等,这些函数都包含在索引节点操作表中,因此索引节点操作表(inode_operations)是第三个需要创建的数据结构。

        为了提高文件系统的读写效率,Linux内核设计了I/O缓存机制。所有的数据无论出入都会经过系统管理的缓冲区,不过,对基于非块的文件系统则可跳过该机制。页缓冲区同样提供了页缓冲区操作表(address_space_operations),其中包含有readpage()、writepage()等函数负责对页缓冲区中的页进行读写等操作。

        文件系统最终要和用户交互,这是通过文件操作表(file_operations)完成的,该表中包含有关用户读写文件、打开、关闭、映射文件等用户接口。

        一般来说,基于块的文件系统的实现都离不开以上5种数据结构。但根据文件系统的特点(如有的文件系统只可读、有的没有目录),并非要实现操作表中的全部函数,因为有的函数系统已经实现,而有的函数不必实现。 

        2、Romfs 文件系统

        Romfs 是一种相对简单,占用空间较少的文件系统。它是只读的文件系统,禁止写操作,因此系统同时需要虚拟盘(RAMDISK)支持临时文件和数据文件的存储。但是 Romfs 支持的最大文件不超过 256MB。

        3、Romfs 文件系统布局与文件结构

        由于Romfs小型、轻量,所以常常用在嵌入系统和系统引导时,它的文件布局比Ext2等文件系统要简单得多。(具体的实现,在我的另外一篇博客里)

        

 

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值