免费注册 查看新帖 |

Chinaunix

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

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

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-09-10 23:24 |只看该作者 |倒序浏览
前面说过,jbd2的日志系统有两种情形,一种是在分区里建一个.journal目录来记录日志,另一种是用独立的分区,相对来说,用.journal目录的方式要简单一些,所以我讲用独立分区的,这样更接近大型系统的实际应用。
下面我们来看看ext4_get_dev_journal(fs/ext4/super.c(2938))

static journal_t *ext4_get_dev_journal(struct super_block *sb,
                 dev_t j_dev)
{
    struct buffer_head *bh;
    journal_t *journal;
    ext4_fsblk_t start;
    ext4_fsblk_t len;
    int hblock, blocksize;
    ext4_fsblk_t sb_block;
    unsigned long offset;
    struct ext4_super_block *es;
    struct block_device *bdev;
    //检查这个分区是不是有兼容功能,如果没有,并且有日志,那就报错。
    BUG_ON(!EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_HAS_JOURNAL));
    // 这个函数是根据设备号获得设备,这个函数也是相当复杂的,这里就不讲了。
    // 如果要详细讲来,10篇blog也写不完。它就是根据设备编号获得一个描述设备
    //的结构体
    bdev = ext4_blkdev_get(j_dev);
    if (bdev == NULL) //当然如果得到的空,就是出错了,我们也就不费劲,直接返回。
        return NULL;
    /*
    从那个bd_claim的函数名,我们就可以宣称这个设备或设备分区的的所有都就是sb,
    如果当前设备已经属于其它的所有者,并且不是当然sb,那么就出错了。然后调用
    blkdev_put来释放当前设备。为什么不直接free掉呢,因为在linux内核分配的内存
    不像我们做一般的开发,有可能还有其它的使用者,在这里调用就把它的bd_openers
    减1,如果为0,表示没有使用者才真正释放。然后函数返回。
    */
    if (bd_claim(bdev, sb)) {
        printk(KERN_ERR
            "EXT4-fs: failed to claim external journal device.\n");
        blkdev_put(bdev, FMODE_READ|FMODE_WRITE);
        return NULL;
    }
    /*
    以下的代码是获得设备分区文件系统块大小和设备的物理块大小。在设备物理特性中,
    能处理的单元最小一般是512字节,当然也有不按常理出牌的家伙。
    */
    blocksize = sb->s_blocksize;
    hblock = bdev_hardsect_size(bdev);
    //如果文件系统的块比设备的物理扇区还小,那就没法操作了,因为硬盘无法操作
    //比扇区还小的单元。那么就退出。
    if (blocksize  hblock) {
        printk(KERN_ERR
            "EXT4-fs: blocksize too small for journal device.\n");
        goto out_bdev;
    }
    sb_block = EXT4_MIN_BLOCK_SIZE / blocksize;
    offset = EXT4_MIN_BLOCK_SIZE % blocksize;
    set_blocksize(bdev, blocksize);
    /*
    __bread 函数就是从设备指定块,读取指定大小的数据,这里是从第一个块读取
    blocksize(通常是4K)的数据,在放在一个buffer_head的结构体中,这个结构
    体在include/linux/buffer_head.h里定义,就是用来存放块设备数据的。
    */
    if (!(bh = __bread(bdev, sb_block, blocksize))) {
        printk(KERN_ERR "EXT4-fs: couldn't read superblock of "
         "external journal\n");
        goto out_bdev;
    }
    /*
    然后再用刚才读出来的数据转换成一个ext4_super_block结构体,是ext4超级块在硬盘上
    的表示。这个结构体包含了分区的详细信息来用构造VFS块的super_block。
    */
    es = (struct ext4_super_block *) (((char *)bh->b_data) + offset);
    // 检查这个超级块是不是ext4的超级块,并且检查它的兼容性。
    if ((le16_to_cpu(es->s_magic) != EXT4_SUPER_MAGIC) ||
     !(le32_to_cpu(es->s_feature_incompat) &
     EXT4_FEATURE_INCOMPAT_JOURNAL_DEV)) {
        printk(KERN_ERR "EXT4-fs: external journal has "
                    "bad superblock\n");
        //如果出错,那么就释放,掉刚读出来的buffer_head,和刚才bdev一样,不能直接free.
        brelse(bh);
        goto out_bdev;
    }
    // 光是前面的检查不够,还要检查这个分区的ext4_super_block里的s_journal_uuid
    // 和刚读出来的(日志分区的)ext4_super_block里的s_uuid是否一样。如果一样才
    // 表示这个分区是正确的journal就是这个日志分区.
    if (memcmp(EXT4_SB(sb)->s_es->s_journal_uuid, es->s_uuid, 16)) {
        printk(KERN_ERR "EXT4-fs: journal UUID does not match\n");
        brelse(bh);
        goto out_bdev;
    }
    // 获得分区的block数,和分区数据开始的block号。因为第一个block用来存放ext4_super_block
    // 结构体所以要加1
    len = ext4_blocks_count(es);
    start = sb_block + 1;
    brelse(bh);    /* we're done with the superblock */
    // 这是这个函数的里的核心内容,主要就是初始化这个分区为日志分区,因为在之前已经做好一切
    // 准备,下一节再做详细的说明。
    journal = jbd2_journal_init_dev(bdev, sb->s_bdev,
                    start, len, blocksize);
    //如果初始没有得到一个真正的journal,那么我们也是返回去了。
    if (!journal) {
        printk(KERN_ERR "EXT4-fs: failed to create device journal\n");
        goto out_bdev;
    }
    // 把super_block放在journal的private数据里,以供以后用。
    journal->j_private = sb;
   
    // 从设备中读取journal的super_block的数据。读一个buffer_head结构表示的数据,
    // ll_rw_block可谓是骨灰的函数,从linux 0.1开始就存在。
    ll_rw_block(READ, 1, &journal->j_sb_buffer);
    // 因为硬盘读数据是异步的,所以要等待,不要看这个函数很简单,它可涉及linux进程
    // 管理最精华的思想,因为这里不是主要讲它的,所以知道ll_rw_block和这个wait_on_buffer
    // 干的事情就是把读的请求通过request提交到这块硬盘的驱动,然后把当前进程挂起,然后等待
    // 数据读完到断续执行。
    wait_on_buffer(journal->j_sb_buffer);
   
    // 如果等待超时,或是读取有错,那j_sb_buffer就不会把b_state置位BH_Uptodate,说明出错了,
    // 当然退出了,还有什么好办法。
    if (!buffer_uptodate(journal->j_sb_buffer)) {
        printk(KERN_ERR "EXT4-fs: I/O error on journal device\n");
        goto out_journal;
    }
   
    // 还要检查是不是只当前进程在用,如果其它进程也在用,或是根本没进程用,那么就出错了,
    // 退出。
    if (be32_to_cpu(journal->j_superblock->s_nr_users) != 1) {
        printk(KERN_ERR "EXT4-fs: External journal has more than one "
                    "user (unsupported) - %d\n",
            be32_to_cpu(journal->j_superblock->s_nr_users));
        goto out_journal;
    }
    // 设置当前分区的日志分区设备描述符。
    EXT4_SB(sb)->journal_bdev = bdev;
    // 初始化journal的参数,主要把 journal通过这个分区的super_block与这个分区关系起来。
    ext4_init_journal_params(sb, journal);
    return journal; // 到这里就大事大吉,收工。
out_journal:
    jbd2_journal_destroy(journal);
out_bdev:
    ext4_blkdev_put(bdev);
    return NULL;
}
这个函数讲完了,就知道一个分区的journal建立过程,当然这里讲的是独分区的日志,同分区目录的日志也差不多,这个都搞明白了,那个不难了。


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

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP