Chinaunix首页 | 论坛 | 博客
  • 博客访问: 39384
  • 博文数量: 9
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 40
  • 用 户 组: 普通用户
  • 注册时间: 2015-06-17 14:56
文章分类

全部博文(9)

文章存档

2015年(9)

我的朋友

分类: LINUX

2015-06-17 14:59:03

前面说过,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建立过程,当然这里讲的是独分区的日志,同分区目录的日志也差不多,这个都搞明白了,那个不难了。

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