linux文件系统的四大对象

    本来想看驱动的,但发现要对驱动有底,那还得全方位的了解文件系统。

1   inode 简介

            inode 是 UNIX/Linux 操作系统中的一种数据结构,其本质是结构体,它包含了与文件系统中各个文件相关的一些重要信息,例如文件及目录的基本信息,包含时间、档名、使用者及群组等。在 UNIX/Linux中创建文件系统时,同时将会创建大量的 inode 。通常,文件系统磁盘空间中大约百分之一空间分配给了 inode 表。在Linux系统中,内核为每一个新创建的文件分配一个Inode(索引结点),每个文件都有一个惟一的inode号,我们可以将inode简单理解成一个指针,它永远指向本文件的具体存储位置。文件属性保存在索引结点里,在访问文件时,索引结点被复制到内存在,从而实现文件的快速访问。系统是通过索引节点(而不是文件名)来定位每一个文件。

Block 是记录文件内容数据的区域,至于 inode 则是记录"该文件的相关属性,以及文件内容放置在哪一个 Block 之内"的信息,换句说, inode 除了记录文件的属性外,同时还必须要具有指向( pointer )的功能,亦即指向文件内容放置的区块之中,好让操作系统可以正确的去取得文件的内容,底下几个是 inode 记录的信息(当然不止这些):
          该文件的拥有者与群组(owner/group);   该文件的存取模式(read/write/excute);    该文件的类型(type);该文件建立或状态改变的时间(ctime)、最近一次的读取时间(atime)、最近修改的时间(mtime);该文件的容量;定义文件特性的旗标(flag),如 SetUID...;该文件真正内容的指向 (pointer); 文件所使用的磁盘块的实际数目;

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;            /* 文件锁链表 */
        struct address_space    *i_mapping;          /* 相关的地址映射 */
        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;
};

2  dentry简介

dentry是Linux文件系统中某个索引节点(inode)的链接,每个dentry代表路径中的一个特定部分。这个索引节点可以是文件,也可以是目录。inode(可理解为ext2 inode)对应于物理磁盘上的具体对象,dentry是一个内存实体,没有对应的磁盘数据结构,VFS根据字符串形式的路径名现场创建他,在dentry中,d_inode成员指向对应的inode。也就是说,一个inode可以在运行的时候链接多个dentry,而d_count记录了这个链接的数量。另外dentry对象有三种状态:被使用,未被使用和负状态。

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

一个有效的dentry结构必定有一个inode结构,这是因为一个目录项要么代表着一个文件,要么代表着一个目录,而目录实际上也是文件。所以,只要dentry结构是有效的,则其指针d_inode必定指向一个inode结构。可是,反过来则不然,一个inode却可能对应着不止一个dentry结构;也就是说,一个文件可以有不止一个文件名或路径名。这是因为一个已经建立的文件可以被连接(link)到其他文件名。所以在inode结构中有一个队列i_dentry,凡是代表着同一个文件的所有目录项都通过其dentry结构中的d_alias域挂入相应inode结构中的i_dentry队列。

在内核中有一个哈希表dentry_hashtable ,是一个list_head的指针数组。一旦在内存中建立起一个目录节点的dentry 结构,该dentry结构就通过其d_hash域链入哈希表中的某个队列中。

内核中还有一个队列dentry_unused,凡是已经没有用户(count域为0)使用的dentry结构就通过其d_lru域挂入这个队列。

Dentry结构中除了d_alias 、d_hash、d_lru三个队列外,还有d_vfsmnt、d_child及d_subdir三个队列。其中d_vfsmnt仅在该dentry为一个安装点时才使用。另外,当该目录节点有父目录时,则其dentry结构就通过d_child挂入其父节点的d_subdirs队列中,同时又通过指针d_parent指向其父目录的dentry结构,而它自己各个子目录的dentry结构则挂在其d_subdirs域指向的队列中。

从上面的叙述可以看出,一个文件系统中所有目录项结构或组织为一个哈希表,或组织为一颗树,或按照某种需要组织为一个链表,这将为文件访问和文件路径搜索奠定下良好的基础。

struct dentry {
   
    unsigned int d_flags;       
    seqcount_t d_seq;       
    struct hlist_bl_node d_hash;    
    struct dentry *d_parent;   
    struct qstr d_name;
    struct inode *d_inode;       
                    
    unsigned char d_iname[DNAME_INLINE_LEN];   

    /* Ref lookup also touches following */

    unsigned int d_count;   引用计数    
    spinlock_t d_lock;       
    const struct dentry_operations *d_op;
    struct super_block *d_sb;    
    unsigned long d_time;       
    void *d_fsdata;           

    struct list_head d_lru;    
   
    /*
     * 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 list_head d_alias;    /* inode alias list */
};

d_count:引用计数

d_flags:目录项缓存标识,可取DCACHE_UNUSED、DCACHE_REFERENCED等

d_inode:与该目录项关联的inode

d_parent:父目录的目录项

d_hash:内核使用dentry_hashtable对dentry进行管理,dentry_hashtable是由list_head组成的链表,一个dentry创建之后,就通过

d_hash链接进入对应的hash值的链表中。

d_lru:最近未使用的目录项的链表

d_child:目录项通过这个加入到父目录的d_subdirs中

d_subdirs:本目录的所有孩子目录链表头
d_alias:一个有效的dentry必然与一个inode关联,但是一个inode可以对应多个dentry,因为一个文件可以被链接到其他文件,所以,这个dentry就是通过这个字段链接到属于自己的inode结构中的i_dentry链表中的。(inode中讲过)

d_mounted:安装在该目录的文件系统的数量!注意一个文件目录下可以有不同的文件系统!

d_name:目录项名称

d_time:重新变为有效的时间!注意只要操作成功这个dentry就是有效的,否则无效。

d_op:目录项操作

d_sb:这个目录项所属的文件系统的超级块

d_vfs_flags:一些标志

d_fsdata:文件系统私有数据

d_iname:存放短的文件名
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]; //存放短文件名
};

3  file简介(文件记录表)

文件对象表示进程已打开的文件,该对象file(不是物理文件)由相应的open()系统调用创建,有close()系统调用销毁,因为多个进程可以同时打开和操作一个文件,所以同一个文件也可能存在多个对应的文件对象。文件对象仅仅在进程观点上代表已打开文件,它反过来指向目录项对象(反过来指向索引节点),其实只有目录项对象才表示已打开的实际文件,虽然一个文件对应的文件对象不是是唯一的,但对应的索引节点和目录项对象无疑是唯一的,另外类似于目录项对象,文件对象实际上没有对应的磁盘数据。

一个文件对象是由一个文件结构体表示的,文件结构体代表一个打开的文件,系统中的每个打开的文件在内核空间都有一个关联的 struct 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  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];     /* 文件对象指针的初始化数组*/}; 

fd域指向文件对象的指针数组。该数组的长度存放在max_fds域中。通常,fd域指向files_struct结构的fd_array域,该域包括32个文件对象指针。如果进程打开的文件数目多于32,内核就分配一个新的、更大的文件指针数组,并将其地址存放在fd域中;内核同时也更新max_fds域的值。

对于在fd数组中有入口地址的每个文件来说,数组的索引就是文件描述符(file descriptor)。通常,数组的第一个元素(索引为0)是进程的标准输入文件,数组的第二个元素(索引为1)是进程的标准输出文件,数组的第三个元素(索引为2)是进程的标准错误文件。请注意,借助于dup()、dup2()和
fcntl ) 系统调用,两个文件描述符就可以指向同一个打开的文件,也就是说,数组的两个元素可能指向同一个文件对象。当用户使用shell结构(如2>&;1)将标准错误文件重定向到标准输出文件上时,用户总能看到这一点。

open_fds域包含open_fds_init域的地址,open_fds_init域表示当前已打开文件的文件描述符的位图。max_fdset域存放位图中的位数。由于数据结构fd_set有1024位,通常不需要扩大位图的大小。不过,如果确实必须的话,内核仍能动态增加位图的大小,这非常类似文件对象的数组的情形。

当开始使用一个文件对象时调用内核提供的fget()函数。这个函数接收文件描述符fd作为参数,返回在current->files->fd[fd]中的地址,即对应文件对象的地址,如果没有任何文件与fd对应,则返回NULL。在第一种情况下,fget()使文件对象引用计数器f_count的值增1。

用户打开文件表files_structs是由进程描述符task_struct中的file域指向,所有与进程相关的信息如打开的文件及文件描述符都包含其中,又从前面可以看出,files_struct通过**file保持对文件对象file的访问,于此相似文件对象file的结构体内成员中包含目录项dentry,目录项dentry将文件名与inode相连,最终通过inode中的指针可以访问存储实际的数据的地方Data Area.

so他们管理层次关系关系如下:进程->task_struct->files_struct->file->dentry->inode->Data Area

猜你喜欢

转载自blog.csdn.net/h490516509/article/details/85208203