dentry 和inode整理
dentry
dentry是一个内存实体,其中的d_inode成员指向对应的inode
struct dentry {
atomic_t d_count;
struct inode * d_inode; //指向一个inode结构。这个inode和dentry共同描述了一个普通文件或者目录文件
struct dentry * d_parent; //父目录的目录项对象
struct list_head d_hash; //链接到dentry cache的hash链表。
struct list_head d_lru; //未使用链表的指针
struct list_head d_child;//dentry自身的链表头,需要链接到父dentry的d_subdirs成员
struct list_head d_subdirs;//项(子项可能是目录,也可能是文件)的链表头,所有的子项都要链接到这个链表
int d_mounted;// 指示dentry是否是一个挂载点。如果是挂载点,该成员不为零。
struct qstr d_name;// 成员保存的是文件或者目录的名字。打开一个文件的时候,根据这个成员和用户输入的名字比较来搜寻目标文件
...
};
当移动文件的时候,需要把一个dentry结构从旧的父dentry的链表上脱离,然后链接到新的父dentry的d_subdirs成员。这样dentry结构之间就构成了一颗目录树
每个dentry都有一个指向其父目录的指针(d_parent),一个子dentry的哈希列表(d_child)。其中,子dentry基本上就是目录中的文件
dentry状态:
1.d_count=0,未使用(unused)状态,d_inode指向相关的的索引节点。该目录项仍然包含有效的信息,只是当前没有人引用他。这种dentry对象在回收内存时可能会被释放。
2.d_count>0,正在使用(inuse)状态,d_inode指向相关的inode对象。这种dentry对象不能被释放。
3.d_count<0,y负(negative)状态,inode对象不复存在可能被删除,dentry对象的d_inode指针为NULL。但这种dentry对象仍然保存在dcache中,以便后续对同一文件名的查找能够快速完成。这种dentry对象在回收内存时将首先被释放。
dentry与dentry_cache
dentry_cache(dcache,目录项高速缓存)由两个数据结构组成:
1、哈希链表dentry_hashtable:dcache中的所有dentry对象都通过d_hash指针域链到相应的dentry哈希链表中。
2、未使用的dentry对象链表dentry_unused:dcache中所有处于unused状态和negative状态的dentry对象都通过其d_lru指针域链入dentry_unused链表中。该链表也称为LRU链表。
dcache中的dentry对象控制着icache中的inode对象的生命期转换。无论何时,只要一个目录项对象存在于dcache中(非 negative状态),则相应的inode就将总是存在,因为 inode的引用计数i_count总是大于0。当dcache中的一个dentry被释放时,针对相应inode对象的iput()方法就会被调用。
inode(索引节点)
文件储存在硬盘上,硬盘的最小存储单位叫做”扇区”(Sector)。每个扇区储存512字节(相当于0.5KB)。操作系统读磁盘时会多个扇区组成一个块(block)一起读取,最常见大小为4kb,8个扇区组成一个块。inode储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等。inode对应于物理磁盘上的具体对象,一个inode可以在运行的时候链接多个dentry,d_count记录了这个链接的数量
struct ext3_inode {
__le16 i_mode; /* 文件的属性(读、写、执行权限)*/
__le16 i_uid; /* 文件拥有者的User ID */
__le32 i_size; /* 文件的字节数*/
__le32 i_atime; /* atime指文件上一次打开的时间 */
__le32 i_ctime; /* 文件的时间戳ctime指inode上一次变动的时间 */
__le32 i_mtime; /* mtime指文件内容上一次变动的时间 */
__le16 i_gid; /* 文件的Group ID*/
__le16 i_links_count; /* 链接数,即有多少文件名指向这个
inode */
......
__le32 i_block[EXT2_N_BLOCKS];/*文件数据block的位置*/
......
}
可以用stat命令,查看某个文件的inode信息:
stat example.txt
函数得到当前文件或目录的inode值后,进入dcache查找对应的dentry,然后顺着父目录指针d_parent得到父目录的dentry,这样逐级向上直到dentry= root,就得到全部目录名称。
inode的大小
inode会消耗硬盘空间,所以硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。
查看每个硬盘分区的inode总数和已经使用的数量,可以使用df命令。
df -i
sudo dumpe2fs -h /dev/hda | grep “Inode size” //查看每个inode节点的大小
inode号码
每个inode都有一个号码,操作系统用inode号码来识别不同的文件。
移动文件或重命名文件,只是改变文件名,不影响inode号码。
打开一个文件以后,系统就以inode号码来识别这个文件,而系统无法从inode号码得知文件名。
ls -i a.cpp //查看文件名对应的inode号码
硬链接就是多个文件名指向同一个inode号码。可以用不同的文件名访问同样的内容;对文件内容进行修改,会影响到所有文件名;但是,删除一个文件名,不影响另一个文件名的访问。
ln 源文件 目标文件 //创建硬链接
两文件指向同一个inode。d_count加1。删除一个文件名,d_count减1。当这个值减到0,表明没有文件名指向这个inode,系统就会回收这个inode号码,以及其所对应block区域。
创建目录时,默认会生成两个目录项:”.”和”..”。前者的inode号码就是当前目录的inode号码,等同于当前目录的”硬链接”;后者的inode号码就是当前目录的父目录的inode号码,等同于父目录的”硬链接”。所以,任何一个目录的”硬链接”总数,总是等于2加上它的子目录总数(含隐藏目录),这里的2是父目录对其的“硬链接”和当前目录下的”.硬链接“。
软连接就像int &a=b,虽然名字不一样,文件A的内容是文件B的路径。读取文件A时,系统会自动将访问者导向文件B。如果删除了文件B,打开文件A就会报错。文件A指向文件B的文件名,而不是文件B的inode号码,文件B的inode”链接数”不会因此发生变化。
ln -s 源文文件或目录 目标文件或目录 //创建软链接
1
文件名包含特殊字符,无法正常删除,直接删除inode节点,就能起到删除文件的作用。
1、根据文件名,通过Directory里的对应关系,找到文件对应的Inode number
2、再根据Inode number读取到文件的Inode table
3、再根据Inode table中的Pointer读取到相应的Blocks