- 论坛徽章:
- 0
|
前面说过,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 |
|