dentry与inode

dentry与inode

首先看dentry数据结构。位于include/linux/dcache.h中 struct dentry

ps:dentry虽然是目录的意思,但是在vfs中,目录和文件都有自己的dentry。(dentry中存了文件名,同一文件存在别名就是这个结构实现的)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

struct dentry {

atomic_td_count;目录项对象使用计数器

unsignedintd_flags;目录项标志

structinode*d_inode;与文件名关联的索引节点

structdentry*d_parent;父目录的目录项对象

structlist_headd_hash;散列表表项的指针

structlist_headd_lru;未使用链表的指针

structlist_headd_child;父目录中目录项对象的链表的指针

structlist_headd_subdirs;对目录而言,表示子目录目录项对象的链表

structlist_headd_alias;相关索引节点(别名)的链表

intd_mounted;对于安装点而言,表示被安装文件系统根项

structqstrd_name;文件名

unsignedlongd_time;/*usedbyd_revalidate*/

structdentry_operations*d_op;目录项方法

structsuper_block*d_sb;文件的超级块对象

vunsignedlongd_vfs_flags;

void*d_fsdata;与文件系统相关的数据

unsignedchard_iname[DNAME_INLINE_LEN];存放短文件名

};

再看inode数据结构。include/linux/fs.h

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

struct inode {/*vfs中数据结构 数据结构中各项的含义见 Linux 内核 465页*/

    struct hlist_node   i_hash;

    struct list_head    i_list;     /* backing dev IO list */

    struct list_head    i_sb_list;

    struct list_head    i_dentry; /*引用该索引节点的目录项对象链表表头*/

    unsigned long       i_ino;/*索引节点号*/

    atomic_t        i_count;

    unsigned int        i_nlink;

    uid_t           i_uid;

    gid_t           i_gid;

    dev_t           i_rdev;

    unsigned int        i_blkbits;

    u64         i_version;

    loff_t          i_size;

#ifdef __NEED_I_SIZE_ORDERED

    seqcount_t      i_size_seqcount;

#endif

    struct timespec     i_atime;

    struct timespec     i_mtime;

    struct timespec     i_ctime;

    blkcnt_t        i_blocks;

    unsigned short          i_bytes;

    umode_t         i_mode;

    spinlock_t      i_lock; /* i_blocks, i_bytes, maybe i_size */

    struct mutex        i_mutex;

    struct rw_semaphore i_alloc_sem;

    const struct inode_operations   *i_op;/*与索引结点有关的操作*/

    const struct file_operations    *i_fop; /* former ->i_op->default_file_ops */

    struct super_block  *i_sb;

    struct file_lock    *i_flock;

    struct address_space    *i_mapping;

    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_mark_entries; /* fsnotify mark entries */

#endif

 

#ifdef CONFIG_INOTIFY

    struct list_head    inotify_watches; /* watches on this inode */

    struct mutex        inotify_mutex;  /* protects the watches list */

#endif

 

    unsigned long       i_state;

    unsigned long       dirtied_when;   /* jiffies of first dirtying */

 

    unsigned int        i_flags;

 

    atomic_t        i_writecount;

#ifdef CONFIG_SECURITY

    void            *i_security;

#endif

#ifdef CONFIG_FS_POSIX_ACL

    struct posix_acl    *i_acl;

    struct posix_acl    *i_default_acl;

#endif

    void            *i_private; /* fs or device private pointer */

}

"文件"即按一定形式存储在介质上的信息,该信息包含两方面,其一是存储的数据本身,其二是文件的元信息以及记录数据占用的block的id的索引数据(这些信息都组织在文件对应的inode中)。在内存中,每个文件都有一个inode和dentry结构。dentry记录文件名,上级目录,子目录等信息,正是我们看到的(目录树、文件树)树状结构;inode记录着文件在存储介质上的位置和分布,dentry->d_inode指向对应的inode结构。inode代表物理意义上的文件,通过inode可以得到一个数组,这个数组记录文件内容的位置,若数组为(4,5,9),则对应数据位于硬盘的4,5,9块(这里只是一种简化表示,实际上记录文件内容所在的block的id,并不是一个简单的数据搞定的)。其索引节点号(就是inode号)为inode->ino,根据ino就可以计算出对应硬盘中inode的具体位置。


dentry所描述的是逻辑意义上的文件,所描述的是文件逻辑上的属性,所以dentry在磁盘上没有对应的映像;而inode结构代表的是物理意义上的文件,故磁盘上也有inode结构。

inode结构中有一个队列i_dentry,凡是代表同一个文件的所有目录项都通过d_alias域挂入相应的inode结构中的i_dentry队列。或者说,由于硬链接的存在,同一个文件可能有多种表示形式,可以成为别名,但它们都对应同一个文件,也就对应同一个inode,所以inode中有一个字段来将这些别名串起来,采用的是链表的形式。


进程打开一个文件,就会有一个file结构与之对应,同一个进程可以多次打开同一个文件而得到多个不同的file结构,file结构描述了被打开文件的属性,读写偏移指针等

两个不同的file可以对应同一个dentry结构,进程多次打开一个文件,对应只有一个dentry结构,dentry存的是目录项和对应文件的inode的信息。

在介质中,每个文件对应一个inode结点,每个文件可有多个文件名,即可以通过不同的文件名访问同一个文件,多个文件名对应一个文件的关系就是数据结构中dentry和inode多对一的关系。inode中不存储文件名字,只有节点号,通过节点号(ino),可以找到数据在介质中的具体位置,即通过内存inode结构中的ino可以定位到磁盘上的inode结构;而dentry则保存文件名和对应的节点号(inode号),这样就可以实现不同文件名访问一个inode。不同的dentry是通过ln指令实现的。


dentry树描绘了文件系统目录结构,但整个目录结构不能长驻内存,因为非常大。内存装不下。

初始状态下,系统只有代表根目录的dentry和所指向的inode(在根文件系统中挂载生成)。此时要打开一个文件,文件路径对应的结点不存在,根目录的dentry无法找到需要的子节点。这时需要通过inode->i_op中lookup方法找到inode的子节点,找到后,再创建一个dentry与之关联。

由这个过程可以看出。先有inode,再有dentry。

当生成的dentry无人使用时被释放,d_count字段记录了dentry的引用计数,引用为0时,dentry被释放。这里的释放不是直接销毁,而是将dentry放入一个“最近最少使用”队列。当队列过大,内存紧缺时,dentry才被释放。这个LRU队列就像是一个缓存池, 加速了对重复的路径的访问. 而当dentry被真正释放时, 它所对应的inode将被减引用. 如果引用为0, inode也被释放.

故需找一个文件路径时,有三种情况:

1.dentry引用大于0,直接在dentry树中找

2.dentry不在树种,在lru队列中找,LRU队列中的dentry被散列到一个散列表中,便于查找,若找到,则重新添加到dentry树中。

3.若2也未找到,则去找inode,找到后,再创建对应的dentry

这里补充每个目录项对象可以出于以下四种状态之一:

1:空闲状态  即该目录项不包含有效的信息,且没被VFS使用。

2.未使用状态  该对象的d_count计数为0,但d_inode指向关联的索引节点(inode)

3.正在使用状态  该对象的d_count计数不为0,但d_inode指向关联的索引节点

4.负状态   与目录项关联的索引节点不存在了


mount过程:

linux首先找到磁盘分区的super block,然后通过解析磁盘的inode table与file data。构建出自己的dentry列表和inode列表。VFS是按照Ext的方法进行构建的,两者非常相似。如inode结点。ext_inode结点中的一些成员变量其实是没有用的,如引用计数等,保留目的是为了和vfs-inode保持一致,这样在用ext_inode构建vfs_inode时,就不需要一个个赋值,只需要一次拷贝。

故非ext格式的文件系统,mount的过程会慢一些


根目录有一个dentry结构,而根目录里的文件和目录都链接到这个根dentry;同样的道理,二级目录里的文件和目录链接到二级目录,这样一层层,就形成了一颗dentry树。从树顶可以遍历整个文件系统。

为了加快对dentry的查找,内核使用hash表来缓存dentry,称为dentry cache,dentry一般现在dentry cache中查找(与之对应的,inode也会在内核空间使用hash表进行缓存,文件的内容,则采用页高速缓存page cache来缓存,而使用address_space和基树进行管理page cache中的页)。

dentry结构里有d_subdirs成员和d_child成员。d_subdirs是子项的链表头,所有的子项都要链接在这个链表上,d_child是自身链表头,需要连接到父dentry的d_subdirs成员。

d_parent是指针,指向父dentry结构。

d_hash是连接到dentry cache的hash链表。

d_name保存目录或文件的名字。打开一个文件的时候,根据这个成员和用户输入的名字来搜寻目标。

d_mounted用来指示dentry是否是一个挂载点。如果是,则成员不为零。


dentry的hash定位,通过d_hash()函数将父目录dentry的地址和所要查找的文件名的hash值结合起来,重新计算一个hash值,并根据其定位到dentry_hashtable哈希表中(即定位某个表头,缩小查找范围),该表是dentry缓存一部分,接下来扫描该链表,从中找到目标dentry,若没有找到,则通过real_lookup()函数从磁盘中查找。


目录项对象存放在dentry_cache的slab高速缓存中。故目录项的创建和删除是通过kmem_cache_alloc()和kmem_cache_free()实现的。目录项高速缓存由两种类型数据结构组成:

1.处于正在使用、未使用或负状态的目录项对象的集合。

2.一个散列表。用于hash快速查找给定文件名和目录名对应的目录项对象。散列表是由dentry_hashtable数组实现。数组中每个元素是一个指向链表的指针。

你可能感兴趣的:(文件系统,linux)