上一节,我们讲了怎么初始化journal对像和日志分区的检查,其中说到一个函数
journal = jbd2_journal_init_dev(bdev, sb->s_bdev, start, len, blocksize);
这个函数就是初始化日志分区的。
上面的参数依次是:日志分区描述结构体,主分区描述结构体,主分区数据起始的block编号,主分区的bock数,和主分区的block的大小。
这个函数的实现在fs/jbd2/journal.c(1036)
/* jbd2_journal_init_dev and jbd2_journal_init_inode: * * Create a journal structure assigned some fixed set of disk blocks to * the journal. We don't actually touch those disk blocks yet, but we * need to set up all of the mapping information to tell the journaling * system where the journal blocks are. * */
/* jbd2_journal_init_dev 和 jbd2_journal_init_inode的功能都是一样的, 就是创建journal结构体,并且给它一些固定的成员赋值,主要是主分区的 一些信息。我们并没有进行实际的硬盘读写操作,只是让日志系统知道真正 的日志信息数据块在哪里。 */ /** 下面这段就不翻译了,日志开始已经过过这些参数的意思了。 * journal_t * jbd2_journal_init_dev() - creates and initialises a journal structure * @bdev: Block device on which to create the journal * @fs_dev: Device which hold journalled filesystem for this journal. * @start: Block nr Start of journal. * @len: Length of the journal in blocks. * @blocksize: blocksize of journalling device * * Returns: a newly created journal_t * * * jbd2_journal_init_dev creates a journal which maps a fixed contiguous * range of blocks on an arbitrary block device. * */ journal_t * jbd2_journal_init_dev(struct block_device *bdev, struct block_device *fs_dev, unsigned long long start, int len, int blocksize) { // 给journal_t结构体分配空间,并初始化一些成员变量,主要是等待列表
// 和一些固定的东西,它的实现在journal.c(971)行,那里面有两个函数要
// 简单说明一下,jbd2_journal_init_revoke:这个函数创建和初始化这个
// 日志的撤销表,就是当一个数据块已经正确写到主设备上了,就要把这块
// 数据从日志系统中删除,因为不是立即删除,所以要放在一个表里,等待。
// 还有另一个函数是journal_init_stats:这个函数初始化journal的状态,
// 并创建一个历史列表来记录100个已经完成了的transaction.
journal_t *journal = journal_init_common(); struct buffer_head *bh; char *p; int n;
if (!journal) return NULL;
/* journal descriptor can store up to n blocks -bzzz */ journal->j_blocksize = blocksize; // 记录主分区的block大小。
//初始化主分区在/proc/fs/jbd2/sdax 的目录和里面的info,history.
jbd2_stats_proc_init(journal); // 计算一个block里能在放多少个buffer_head的描述符。并创建相应个数的
// buffer_head的指针空间。
n = journal->j_blocksize / sizeof(journal_block_tag_t); journal->j_wbufsize = n; journal->j_wbuf = kmalloc(n * sizeof(struct buffer_head*), GFP_KERNEL); if (!journal->j_wbuf) { printk(KERN_ERR "%s: Cant allocate bhs for commit thread\n", __func__); goto out_err; } journal->j_dev = bdev; journal->j_fs_dev = fs_dev; journal->j_blk_offset = start; journal->j_maxlen = len; /* 下面就是根据主分区来生成日志的名字,就是设备名加分区名,如“sda8” */ bdevname(journal->j_dev, journal->j_devname); p = journal->j_devname; while ((p = strchr(p, '/'))) *p = '!'; // 从日志分区的第一个数据块读出一个块的数据。这个块就是日志分区的
// super_block,当然实际名称是journal_superblock_t,定义在include/
// linux/jbd2.h(212)
bh = __getblk(journal->j_dev, start, journal->j_blocksize); if (!bh) { printk(KERN_ERR "%s: Cannot get buffer for journal superblock\n", __func__); goto out_err; } journal->j_sb_buffer = bh; journal->j_superblock = (journal_superblock_t *)bh->b_data;
return journal; out_err: jbd2_stats_proc_exit(journal); kfree(journal); return NULL; }
|
接下来我们再看看jbd2_stats_proc_init,其定义在journal.c(927)
/*这个函数就是这么寥寥几行,真让人怀疑它的能力,但你有所不知道,往往最简单的东西 后面却暗藏着玄机,或叫什么冰山一角之类的吧。且让我说说它的功能。确实很简单,就 是在/proc/fs/jbd2目录下根据设备号生成的名称创建一个目录,比如sda8:8(因为我的机器 没有独立分区来记录易日志,而是一个目录,所以在sda8后加了一个8,表示这个分区里的第8个inode是用来记录日志的,相关代码在journal.c/1112),关于里面的proc_jbd2_stats这个变量如果你的大脑内存比较大应该还记得在journal_init函数里有一个 jbd2_create_jbd_stats_proc_entry吧,就是在那创建的这个目录。这里再深入就涉及/proc文件系统了,但是/proc系统作为内核对外层应该的一个窗口,我就不得不哆嗦几句了。创建了相应的目录后,再创建两个文件,一个是history,一个是info,到这里,应该怎么第三节里讲的那两个文件是怎么来的了吧,至于怎么显示出那些信息来的,放在下节讲吧,哥们累了,要睡了Zzzz*/ static void jbd2_stats_proc_init(journal_t *journal) { journal->j_proc_entry = proc_mkdir(journal->j_devname, proc_jbd2_stats); if (journal->j_proc_entry) { proc_create_data("history", S_IRUGO, journal->j_proc_entry, &jbd2_seq_history_fops, journal); proc_create_data("info", S_IRUGO, journal->j_proc_entry, &jbd2_seq_info_fops, journal); } }
|
阅读(1311) | 评论(0) | 转发(0) |