免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 9271 | 回复: 6
打印 上一主题 下一主题

ext3、ext4的orphan inode机制分析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-10-08 20:52 |只看该作者 |倒序浏览
本帖最后由 panweiping 于 2010-10-09 09:24 编辑

一、概述

orphan在英文中是孤儿的意思,在这里取被遗弃、被删除之意。
orphan inode是什么样的inode呢?这种inode是怎样产生的呢?
先介绍一个概念,文件的引用计数,准确地说应该是inode的引用计数,因为一般来说一个文件会对应一个inode。文件的引用计数,简单地说是表示有多少个文件指向该文件,准确地说是文件的硬链接的个数。
情况1:设想一个进程,open一个文件,然后unlink该文件,然后进行文件读写。这是允许的,并且在进程退出时,内核会自动将引用计数为0的文件删除。
但是如果该进程尚未退出之前,系统崩溃了,那么,内核就没有机会将已被unlink、并且引用计数为0的inode从磁盘上删除了。
情况2:设想我们正在截断一个大文件(系统调用truncate),但是操作尚未完成,系统就崩溃了。同样,内核也没有办法将该文件的所有数据块全部删除了。
ext3、ext4的orphan inode机制就是处理上述两种情况的。基本思想是这样的:如果要删除或截断一个inode,要先把这个inode记录到磁盘上的一个特殊的orphan inode链表上。如果删除或截断操作能够正常完成,那么,就从磁盘上的orphan inode链表上删除该inode;否则,如果删除或截断操作未完成之前,系统就发生崩溃了,那么,系统重启后,文件系统会遍历磁盘上的orphan inode链表,对链表上的每一个inode都重新进行一遍删除或截断操作,以此来保证这些inode真正在磁盘上被删除,维护文件系统的一致性。
内核版本:2.6.35

二、相关数据结构及之间的关系

先总体说一下orphan inode的组织。
orphan inode需要在两个地方组织,分别是在内存中和在磁盘上。不论在哪里,从抽象角度来看,orphan inode都被组织成一个单向链表。

1、ext4_inode

struct ext4_inode {

    __le32 i_dtime; /* Deletion Time */

    .........

}
这个是磁盘上的inode的结构,i_dtime本来表示该inode被删除的时间,在orphan inode机制中,因为此时该域的值并不重要,故借用一下,用于记录下一个被unlink/truncate的inode号。

2、ext4_super_block

struct ext4_super_block {

    __le32 s_last_orphan; /* start of list of inodes to delete */

    .........

}
这个是磁盘上的superblock结构。其中,s_last_orphan记录的是最近一个被unlink/truncate的inode号,从抽象角度来看,它就代表磁盘上orphan inode单链表的头。
新的inode插入orphan inode链表时采用“头插法”,也就是说,最近被unlink/truncate的inode号会放在s_last_orphan中。
这样,磁盘上的orphan inode单链表如下图1所示。



3、ext4_inode_info

struct ext4_inode_info {

    __u32 i_dtime;

    struct list_head i_orphan; /* unlinked but open inodes */

    .......

}

这个是内存中的inode结构,即 磁盘上的ext4_inode在内存中的表现。
其中,i_dtime与ext4_inode中的i_dtime相对应,i_orphan是个链表节点,用于在内存中组成orphan
inode链表。

4、ext4_sb_info

struct ext4_sb_info {

    struct list_head s_orphan;

    struct mutex s_orphan_lock;

    .........

}
这个是内存中的superblock结构,即磁盘上ext4_super_block在内存中的表现。其中,s_orphan代表链表头,s_orphan_lock是用于保护链表的互斥锁。在内存中的orphan inode链表结构与图1很类似,在此从略。

三、辅助函数

1、NEXT_ORPHAN(inode)
fs/ext4/ext4.h
1209 #define NEXT_ORPHAN(inode) EXT4_I(inode)->i_dtime
这个宏的作用是在磁盘orphan inode链表上取得下一个orphan inode号。

2、ext4_orphan_get()

1074 struct inode *ext4_orphan_get(struct super_block *sb, unsigned long ino)
{
       .......

1104 inode = ext4_iget(sb, ino);
       .......
}
这个函数的主要作用是根据一个inode号ino,从磁盘上将该inode的信息读入内存,保存在ext4_inode_info结构中。

3、ext4_orphan_add()
1984 int ext4_orphan_add(handle_t *handle, struct inode *inode)
{
       .......
       // 将“下一个”最近的inode号保存在本inode的ext4_inode_info->i_dtime中
       // 这样,当本inode写回磁盘时,会将“下一个”orphan inode号写回磁盘。
2030 NEXT_ORPHAN(inode) = le32_to_cpu(EXT4_SB(sb)->s_es->s_last_orphan);
       // 将最近的inode号保存在ext4_super_block->s_last_orphan中,
       // 这样,当本超级块写回磁盘时,会将最近的orphan inode号写回磁盘。
2031 EXT4_SB(sb)->s_es->s_last_orphan = cpu_to_le32(inode->i_ino);
       .......
       // 将内存中的ext4_inode_info结构链到ext4_sb_info->s_orphan链表的第一个位置。
2046 if (!err)
2047     list_add(&EXT4_I(inode)->i_orphan, &EXT4_SB(sb)->s_orphan);
       .......
}
总之,ext4_orphan_add()的作用是在内存中和磁盘上的orphan inode链表中分别添加一个inode节点。

4、ext4_orphan_del()
2062 int ext4_orphan_del(handle_t *handle, struct inode *inode)
{
       .......
       // 取得磁盘上“下一个”orphan inode号
2079 ino_next = NEXT_ORPHAN(inode);
       // 取得内存中“上一个”orphan inode的节点
2080 prev = ei->i_orphan.prev;
       .......
       // 在内存中的orphan inode链表上将该inode删除
2085 list_del_init(&ei->i_orphan);
       .......
       // 下面的代码为什么会有分支?
       // 主要是因为要区分要删除的inode是不是链表头
2098 if (prev == &sbi->s_orphan) {
            // 如果要删除的inode在链表头
           // ext4_super_block->s_last_orphan中记录该inode的下一个inode号即可
2104     sbi->s_es->s_last_orphan = cpu_to_le32(ino_next);
2106  } else {
             // 否则,要删除的inode不在链表头
             // 用上一个inode的i_dtime记录本inode下一个 inode号
2116       NEXT_ORPHAN(i_prev) = ino_next;
2118 }
        .......
       // 本inode已从orphan链表中删除了,故这里i_dtime设为0
2121 NEXT_ORPHAN(inode) = 0;
       .......
}

总之,ext4_orphan_del()的作用是在内存中和磁盘上的orphan inode链表中分别删除一个inode节点。

四、删除一个inode的正常过程
我们结合unlink系统调用的逻辑,看看删除一个inode的正常的过程。
注意:我们只看未unlink前,该inode的引用计数为1,unlink后,该inode的引用计数为0的情况。因为只有这种情况才会要求真正从磁盘上删除该inode。
SYSCALL_DEFINE1(unlink, const char __user *, pathname)
    -->do_unlinkat
          -->vfs_unlink
                 -->ext4_unlink
                         -->ext4_delete_entry从该文件所在目录中删除该文件
                         -->ext4_orphan_add
          -->iput
                  -->iput_final
                          -->generic_drop_inode
                                  -->generic_delete_inode(inode);
                                          -->ext4_delete_inode
                                                  -->ext4_truncate清除磁盘上的索引信息
                                                  -->ext4_orphan_del
                                                  -->ext4_free_inode从内存中和磁盘上分别删除该inode

由此过程可知,在删除一个inode时,ext4_orphan_add和ext4_orphan_del是成对使用的,
这样才能保证一致性。

五、恢复时处理orphan inode的逻辑
一般而言,上述unlink过程在ext4_delete_inode会执行较长的时间,因为里面有ext4_truncate函数,它负责把inode的全部索引信息清除,如果一个文件比较大,索引信息就会很多,删除过程就会很长。
现在有两个假设:
假设1、假设orphan inode的信息已经写回磁盘,这样磁盘上的inode和superblock就包含了orphan inode的信息。
假设2、假设在ext4_delete_inode函数的执行过程中,系统崩溃了。
此时orphan inode机制就可以发挥作用了。
挂载文件系统会调用下列函数:
ext4_get_sb
       -->get_sb_bdev
               -->ext4_fill_super
                       -->ext4_orphan_cleanup
1970 static void ext4_orphan_cleanup(struct super_block *sb,
1971 struct ext4_super_block *es)
1972 {
             .......
2017      while (es->s_last_orphan) {
                   // 有点小技巧的地方在这里!
                   // es->s_last_orphan明显是个整数,这里怎么弄成个循环呢?
                   // 原因是每次循环,会调用ext4_orphan_del()在orphan链表上删除一个inode,
                   // 注意看ext4_orphan_del()函数的2104行,会将es->s_last_orphan设置成下一个被orphan的inode号。
                   // 于是,本循环的作用是从最近被orphan的inode开始,依次处理次近的、第三近的......最后一个inode。
2018           struct inode *inode;
2019
2020           inode = ext4_orphan_get(sb, le32_to_cpu(es->s_last_orphan));
                    .......
  
                  // 将该inode加入内存中的orphan inode链表
2026           list_add(&EXT4_I(inode)->i_orphan, &EXT4_SB(sb)->s_orphan);
2028           if (inode->i_nlink) {
                      // 如果该inode的引用计数为0,则重新执行一遍 ext4_truncate,
                      // 清除该文件在磁盘上的索引信息
2034               ext4_truncate(inode);
2035               nr_truncates++;
2036           } else {
2042                nr_orphans++;
2043           }

                   // 正如注释中说的那样,真正神奇的地方在这里
                   // 参考第四章“删除一个inode的正常过程”,你就会直到iput最终会调用ext4_orphan_del()在orphan链表上删除一个inode,
                  // 注意看ext4_orphan_del()函数的2104行,
                  // 会将es->s_last_orphan设置成下一个被orphan的inode号,这样循环才能继续。
2044         iput(inode);  /* The delete magic happens here! */
            }
             .......
2045 }

六、后记

至此,orphan inode机制就介绍完了。

总得来说,orphan inode机制能够在一定程度上避免unlink/truncate文件时的不一致性。

这里特别要强调“一定程度”,在第五章中我们做了三个假设,其中,如果假设1不成立的话,

orphan inode机制也是不能发挥作用的,因为必要的信息尚未写入磁盘,重启以后就无据可依了。
ext3、ext4 orphan inode机制分析.pdf (113.35 KB, 下载次数: 235)
ext3、ext4 orphan inode机制分析.odt.gz (28 KB, 下载次数: 95)
ext3、ext4 orphan inode机制分析.doc.gz (8.11 KB, 下载次数: 86)

ext3、ext4 orphan inode机制分析.pdf

108.28 KB, 下载次数: 161

ext3、ext4 orphan inode机制分析.doc.gz

7.86 KB, 下载次数: 77

ext3、ext4 orphan inode机制分析.odt.gz

27.79 KB, 下载次数: 94

评分

参与人数 1可用积分 +18 收起 理由
T-Bagwell + 18 精品文章

查看全部评分

论坛徽章:
0
2 [报告]
发表于 2010-10-09 10:08 |只看该作者
内存里的数据还比较好理解,磁盘上的orphan inode是怎么处理?

文件系统加载时会遍历inode table么?hard link count为0的inode就删掉?

论坛徽章:
0
3 [报告]
发表于 2010-10-09 10:47 |只看该作者
内存里的数据还比较好理解,磁盘上的orphan inode是怎么处理?
这个是我注释的不清,ext4_orphan_cleanup()函数的while循环我又加了点注释,应该比较清楚了。

文件系统加载时会遍历inode table么?
这里我们关注的是orphan inode链表,而不是inode table。
文件系统加载时不会遍历inode table,fsck时会检查inode table。

hard link count为0的inode就删掉?
是的。

论坛徽章:
0
4 [报告]
发表于 2010-10-09 12:22 |只看该作者
好文章,先留名,慢慢看!
感谢楼主!

论坛徽章:
5
摩羯座
日期:2014-07-22 09:03:552015元宵节徽章
日期:2015-03-06 15:50:392015亚冠之大阪钢巴
日期:2015-06-12 16:01:352015年中国系统架构师大会
日期:2015-06-29 16:11:2815-16赛季CBA联赛之四川
日期:2018-12-17 14:10:21
5 [报告]
发表于 2010-11-09 10:30 |只看该作者
看到楼主往ext4提patch了

论坛徽章:
0
6 [报告]
发表于 2010-11-10 09:54 |只看该作者
版主见笑了。起先觉得是ext4代码的问题,后来高人指点,还是自己理解不够全面,才疏学浅了。
希望多认识几个对文件系统感兴趣的同志,大家多交流,多提高。
我觉得仅仅分析代码,提高还是很有限的,最多是“哦,我明白了,不过如此”
但是要能够达到修复bug,增加特性的程度,还有很长的路要走呢。
纸上得来终觉浅,绝知此事要躬行啊。

论坛徽章:
5
摩羯座
日期:2014-07-22 09:03:552015元宵节徽章
日期:2015-03-06 15:50:392015亚冠之大阪钢巴
日期:2015-06-12 16:01:352015年中国系统架构师大会
日期:2015-06-29 16:11:2815-16赛季CBA联赛之四川
日期:2018-12-17 14:10:21
7 [报告]
发表于 2010-11-10 09:58 |只看该作者
本帖最后由 T-Bagwell 于 2010-11-10 10:11 编辑
版主见笑了。起先觉得是ext4代码的问题,后来高人指点,还是自己理解不够全面,才疏学浅了。
希望多认识几 ...
panweiping 发表于 2010-11-10 09:54



    首先得了解整体,然后了解细节都是干什么的,多交流
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP