Chinaunix首页 | 论坛 | 博客
  • 博客访问: 97586
  • 博文数量: 14
  • 博客积分: 601
  • 博客等级: 中士
  • 技术积分: 247
  • 用 户 组: 普通用户
  • 注册时间: 2009-05-05 14:26
文章分类

全部博文(14)

文章存档

2010年(3)

2009年(11)

我的朋友

分类: LINUX

2009-09-09 22:52:14

  在上一节,我们把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/
 
让我们来看看
ls /proc/fs/jbd2/
sda10:8  sda7:8  sda9:8
在jbd2下有三个目录,因为我的机器上有三个ext4分区,分别是sda7/9/10,所以它会在jbd2里创建三个目录,分别代表三个分区,再让我们看看里面买的什么药,
ls /proc/fs/jbd2/sda10\:8/
history  info
是两个文件,遇到文件,我们就cat一下。
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,你就会看一些历史记录
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。

阅读(5432) | 评论(0) | 转发(3) |
给主人留下些什么吧!~~