inode仅仅只是保存了文件对象的属性信息,包括:权限、属组、数据块的位置、时间戳等信息。但是并没有包含文件名,文件在文件系统的目录树中所处的位置信息。那么内核又是怎么管理文件系统的目录树呢?答案是目录项。
目录项在内核中起到了连接不同的文件对象inode的作用,进而起到了维护文件系统目录树的作用。dentry是一个纯粹的内存结构,由文件系统在提供文件访问的过程中在内存中直接建立(并没有实际对应的磁盘上的描述)。
struct dentry
{
/* RCUlookup touched fields */
unsignedint d_flags;/*protected by d_lock */
seqcount_t d_seq;/*per dentry seqlock */
struct hlist_bl_noded_hash;/* lookup hash list */
struct dentry *d_parent;/*parent directory */
struct qstrd_name; //目录项名称
struct inode *d_inode; /* Where the name belongsto - NULL is negative */
unsignedchar d_iname[DNAME_INLINE_LEN];/*small names */
/* Reflookup also touches following */
struct lockrefd_lockref;/* per-dentry lock and refcount */
conststruct dentry_operations *d_op;
struct super_block *d_sb;/*The root of the dentry tree */
unsignedlong d_time;/*used by d_revalidate */
void*d_fsdata;/*fs-specific data */
struct list_headd_lru;/* LRU list */
struct list_headd_child;/* child of parent list */
struct list_headd_subdirs;/* our children */
/*
*d_alias and d_rcu can share memory
*/
union
{
struct hlist_noded_alias;/* inode alias list */
struct rcu_headd_rcu;
} d_u;
};
在内存中,每个文件都有一个dentry(目录项),dentry记录文件名、父目录、子目录等信息,形成文件树结构。而且每个dentry都有一个唯一对应的inode,而每个inode则可能有多个dentry(这种情况是由ln硬链接产生的)。
[root@localhostnewtest]# tree
.
├──subtest
│ └──subtest.py
└──test.py
1 directory, 2 files
[root@localhostnewtest]# ls -i
131074subtest 131075 test.py
[root@localhostnewtest]#
[root@localhostnewtest]# ls -i subtest/subtest.py
131076subtest/subtest.py
Unix/Linux系统中,目录(directory)也是一种文件。打开目录,实际上就是打开目录文件。
文件读取过程
文件由文件名、文件属性和数据组成。文件的inode中不包含文件的名字,文件的名字位于存放文件所在的目录中,在目录中通过查看目录项,因为每个目录项包含两项信息,即这个目录中的文件名字及对应的inode编号,通过这种对应关系找到文件。
具体来说VFS在查找的时候,根据一层一层的目录项找到对应的每个目录项的inode,那么沿着目录项进行操作就可以找到最终的文件。又inode储存有一些指针,这些指针指向存储设备中的一些数据块,文件的内容就储存在这些数据块中。当Linux想要打开一个文件时,只需要找到文件对应的inode,然后沿着指针,将所有的数据块收集起来,就可以在内存中组成一个文件的数据了。
image.png例如:cat一个文件/newtest/subtest/subtest.py时(/、newtest、subtest、subtest.py都是一个目录项),它的查找过程如下
系统通过挂载信息(在超级块中,位置固定)找到根目录(/)的inode编号,根目录对应的inode是固定的。在dentry中找到对应的inode信息,从inode信息中找到存储根目录信息的数据块。
从目录块中存储的信息中,找到文件名(目录名)为newtest所对应的inode编号,如
[[root@localhostnewtest]# ls -i /
17 bin 1025 dev 13 lib 11 lost+found 131073
newtest 786433 root 2883585 srv 262145 tmp
2 boot 4325377 etc 15 lib64 1048577 media 5242881 opt 8199 run 1 sys 6029313 usr
7995393d1 4980737 home 1310721 logs 3145729 mnt 1 proc 16 sbin 655361 test 917505 var
[root@localhostnewtest]#
找到newtest的inode编号后,从该inode信息中,找到newtest目录存放的数据块,再从该数据块存储的信息中,找到subtest目录对应的inode编号
[root@localhostnewtest]# ls -i
131074 subtest 131075 test.py
重复上述步骤,直至找到test.py文件对应的inode结点,根据inode结点中记录的message文件内容对应的数据块,从数据块中读取内容。
写入文件的过程
当我们写入一个文件时,是分配一个空白inode给该文件,将其inode编号记入该文件所属的目录,然后选取空白的数据块,让inode的指针指像这些数据块,并放入内存中的数据。
删除文件
实质上就是减少link count,当link count为0时,就表示这个Inode可以使用,并把Block标记为可以写,但并没有清除Block里面数据,除非是有新的数据需要用到这个block。
网友评论