免费注册 查看新帖 |

Chinaunix

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

JBD2(journaling block device 2)系统分析(四) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-09-09 22:52 |只看该作者 |倒序浏览
  在上一节,我们把jbd2的初始化代码讲完了,初始代码的结果是创建了四个cache,
jbd2_revoke_record_cache ----- struct jbd2_revoke_record_s
jbd2_revoke_table_cache ------ struct jbd2_revoke_table_s
jbd2_journal_head_cache ------ struct journal_head
jbd2_handle_cache       ------ handle_t (struct handle_s)
还有一个目录
/proc/jbd2/

让我们来看看
[email=lan@lan-laptop:~$]lan@lan-laptop:~$[/email]
ls /proc/fs/jbd2/
sda10:8  sda7:8  sda9:8
在jbd2下有三个目录,因为我的机器上有三个ext4分区,分别是sda7/9/10,所以它会在jbd2里创建三个目录,分别代表三个分区,再让我们看看里面买的什么药,
[email=lan@lan-laptop:~$]lan@lan-laptop:~$[/email]
ls /proc/fs/jbd2/sda10\:8/
history  info
是两个文件,遇到文件,我们就cat一下。
[email=lan@lan-laptop:~$]lan@lan-laptop:~$[/email]
cat /proc/fs/jbd2/sda10\:8/info
48 transaction, each upto 8192 blocks
average:
  0ms waiting for transaction
  2396ms running transaction
  0ms transaction was being locked
  0ms flushing data (in ordered mode)
  60ms logging transaction
  36369us average transaction commit time
  23 handles per transaction
  8 blocks per transaction
  9 logged blocks per transaction
如果你不是英文差到连牛A与牛C之间是什么都不知道的话,你应该能看懂上面的洋文,就是一些关于这个分区日志的基本信息,如果我们也cat一下history,你就会看一些历史记录
[email=lan@lan-laptop:~$]lan@lan-laptop:~$[/email]
cat /proc/fs/jbd2/sda10\:8/history
R/C  tid   wait  run   lock  flush log   hndls  block inlog ctime write drop  close
R    8568  0     544   0     0     32    19     9     10   
R    8569  0     5004  0     0     280   212    17    18   
R    8570  0     1588  0     0     76    60     12    13   
………
就是记录一些历史操作的结果。
这些东西是怎么出来的,就让我们来撬开它的大门,因为它在每个分区都有一个,所以在第个分区初始化的时候就把这些东西弄好了。

为了让大家容易理解,我把建立这个功能的函数的调用关系作了一张图


在fs/ext4/super.c(2631)有这么一句

if (ext4_load_journal(sb, es, journal_devnum))
            goto failed_mount3;
这句话是在ext4_fill_super里调用,这个函数是ext4文件系统为每个分区初始化的时候构造 super_block调用的。关于ext4文件系统,我也准备写一个详细的分析,所以在这里不多说了。让我们来看看ext4_load_journal这个函数,在fs/ext4/super.c(3030)

static int ext4_load_journal(struct super_block *sb,
             struct ext4_super_block *es,
             unsigned long journal_devnum)
{
    journal_t *journal;
    unsigned int journal_inum = le32_to_cpu(es->s_journal_inum);
    dev_t journal_dev;
    int err = 0;
    int really_read_only;
    BUG_ON(!EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_HAS_JOURNAL));
    if (journal_devnum &&
     journal_devnum != le32_to_cpu(es->s_journal_dev)) {
        printk(KERN_INFO "EXT4-fs: external journal device major/minor "
            "numbers have changed\n");
        journal_dev = new_decode_dev(journal_devnum);
    } else
        journal_dev = new_decode_dev(le32_to_cpu(es->s_journal_dev));
    really_read_only = bdev_read_only(sb->s_bdev);
    /*
     * Are we loading a blank journal or performing recovery after a
     * crash? For recovery, we need to check in advance whether we
     * can get read-write access to the device.
     */
    if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER)) {
        if (sb->s_flags & MS_RDONLY) {
            printk(KERN_INFO "EXT4-fs: INFO: recovery "
                    "required on readonly filesystem.\n");
            if (really_read_only) {
                printk(KERN_ERR "EXT4-fs: write access "
                    "unavailable, cannot proceed.\n");
                return -EROFS;
            }
            printk(KERN_INFO "EXT4-fs: write access will "
             "be enabled during recovery.\n");
        }
    }
    if (journal_inum && journal_dev) {
        printk(KERN_ERR "EXT4-fs: filesystem has both journal "
         "and inode journals!\n");
        return -EINVAL;
    }
    if (journal_inum) {
        if (!(journal = ext4_get_journal(sb, journal_inum)))
            return -EINVAL;
    } else {
        if (!(journal = ext4_get_dev_journal(sb, journal_dev)))
            return -EINVAL;
    }
    if (journal->j_flags & JBD2_BARRIER)
        printk(KERN_INFO "EXT4-fs: barriers enabled\n");
    else
        printk(KERN_INFO "EXT4-fs: barriers disabled\n");
    if (!really_read_only && test_opt(sb, UPDATE_JOURNAL)) {
        err = jbd2_journal_update_format(journal);
        if (err) {
            printk(KERN_ERR "EXT4-fs: error updating journal.\n");
            jbd2_journal_destroy(journal);
            return err;
        }
    }
    if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER))
        err = jbd2_journal_wipe(journal, !really_read_only);
    if (!err)
        err = jbd2_journal_load(journal);
    if (err) {
        printk(KERN_ERR "EXT4-fs: error loading journal.\n");
        jbd2_journal_destroy(journal);
        return err;
    }
    EXT4_SB(sb)->s_journal = journal;
    ext4_clear_journal_err(sb, es);
    if (journal_devnum &&
     journal_devnum != le32_to_cpu(es->s_journal_dev)) {
        es->s_journal_dev = cpu_to_le32(journal_devnum);
        sb->s_dirt = 1;
        /* Make sure we flush the recovery flag to disk. */
        ext4_commit_super(sb, es, 1);
    }
    return 0;
}
这个函数是我们目前见到最长的函数,且听我慢慢说来。第6行unsigned int journal_inum = le32_to_cpu(es->s_journal_inum);这行的意思是把一个小端(little endian)数转成CPU数,关于大小端,有点计算机道德的人都知道,在计算机处理器中,有大小端之分,简而言之就是假如一个short数0xABCD,如果在计算机内存中AB在放在大的地址CD小的地址,则是大端,比如以前因为Apple闻名的powerPC处理器就是大端的,反过来而是小端,我们所用的Intel的就是小端。因为es是ext4_super_block,是ext4文件系统里的物理结构读出来的是小端,我们为了保证在各种CPU上都能运行,所以就定义了这么一个与CPU相关的宏来转换。这样的函数在以后会经常看到,不重复。这个函数就是读出日志所在的inode节点的编号。
11行就是如果这个分区有兼容的日志系统如是从ext3转过来的,那么就会报一个bug.
13-19行就是根据分区号创建一个日志设备,如果if成立,表示要挂载的分区号和在分区里在放的分区号不一样,当然以现在的分区号为准,如果走else那么就以硬盘存的为准。实际上日志设备并不是一个真正的设备,在通常情况下,它只是硬盘分区里的一个目录。
21-41行就是检查分区是不是只读分区,如果是只读分区,那就没有必要加载日志系统,你不能写,要日志来做什么?
43-55行比较有意思,就是在通常情况下,我们的日志只是一个目录,但是也有些用一个独立的分区来记录日志,所以如果一个分区同时有两种,那么就出错了。如果只有一种,那就根据存在的这种生成一个journal对象,其中ext4_get_journal和ext4_get_dev_journal在下一节会做详细的讲解,因为jbd2的肉体是由他们来构建的。
57-60行就是测试一下这个硬盘是不是开启了硬盘屏障,IDE Barrier是指有些硬盘有很大的容量,但是有些主板不支持那么大的,我记得三年前很多同志的硬盘只能识别137G,就是这个IDE Barrier.
62-69行就是如果分区不是只读,并且需要更新日志,那么就更新,有两种情况,一是日志一到性更新,还有就是把日志的版本更新。
71-72行检查日志如果有不兼容的情况,就要把日志清扫,当然如果分区只读,么就打印一个信息"JBD: Ignoring recovery information on journal",然后就什么也不做,如果是读写分区,那么就会清扫日志,把日志内容清空。
73-74行,把日志信息从分区的super_block中读出来。这个jbd2_journal_load也不是简洁的东西,代码和调用很多,但是没有什么难的东西,最难的东西是它的这用调用关系jbd2_journal_load -> load_superblock -> journal_get_superblock 最后这个函数里第9-15行那点从硬盘读数据的代码, 那个可以先不管,知道是从硬盘读取相应的数据就行了。
85-92行就是根据刚才的情况更新super_block,并把它写到硬盘上。这样这个函数就完成使命了,这篇日志也同样完成使命了。下节开始讲ext4_get_journal和ext4_get_dev_journal。


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/95949/showart_2049382.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP