📄 journal.c.txt
字号:
any problems, send mails to sindybear@163.com
相关文件
********************************日志系统的生成************************
日志的初始化主要是将日志的超级块从磁盘上读出来,日志的超级块都在日志的第一块中。
日志可以使用两个函数进行初始化,一个是journal_init_dev,这个函数是从设备上读出日志的
超级块信息。一个是journal_init_inode,如果日志的组织结构是以文件系统的inode为基础的,
那么就从相应的inode节点中读出相应的日志信息。
journal_t * journal_init_dev(kdev_t dev, kdev_t fs_dev,int start, int len, int blocksize)
journal_t *journal = journal_init_common();
journal->j_dev = dev; //进行一些通常的初始化
journal->j_fs_dev = fs_dev;
journal->j_blk_offset = start;
journal->j_maxlen = len;
journal->j_blocksize = blocksize;
bh = getblk(journal->j_dev, start, journal->j_blocksize);//从设备上读出日志的第一块
journal->j_sb_buffer = bh;
journal->j_superblock = (journal_superblock_t *)bh->b_data;//第一块就包含日志的超级块信息
return journal; //返回生成的日志。
journal_t * journal_init_inode (struct inode *inode)
journal_t *journal = journal_init_common(); //进行普通的日志初始化,
//主要设置等待队列,信号量等
journal->j_dev = inode->i_dev;
journal->j_fs_dev = inode->i_dev;
journal->j_inode = inode; //按照inode进行相应的设置
err = journal_bmap(journal, 0, &blocknr); //得到这个日志逻辑0块在磁盘上的物理块号
bh = getblk(journal->j_dev, blocknr, journal->j_blocksize); //申请这个磁盘块的缓存
journal->j_sb_buffer = bh;
journal->j_superblock = (journal_superblock_t *)bh->b_data; //读出日志的超级块
return journal; //返回生成的日志
int journal_bmap(journal_t *journal, unsigned long blocknr, unsigned long *retp)
ret = bmap(journal->j_inode, blocknr); //得到日志的得逻辑块再磁盘上的物理块号
if (ret)
*retp = ret;
int journal_create (journal_t *journal)
if (journal->j_maxlen < JFS_MIN_JOURNAL_BLOCKS) //如果日志区小于下限,返回错误
return -EINVAL;
for (i = 0; i < journal->j_maxlen; i++) {
err = journal_bmap(journal, i, &blocknr); //得到日志中的数据区的磁盘块号
if (err)
return err;
bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);//按照磁盘块号读出来
memset (bh->b_data, 0, journal->j_blocksize); //全部赋值为0
mark_buffer_dirty(bh); //写到磁盘上
mark_buffer_uptodate(bh, 1);
__brelse(bh);
}
sync_dev(journal->j_dev); //同步这个设备,将上面的数据全都写到磁盘上
…… //设置日志超级块
journal->j_transaction_sequence = 1;
return journal_reset(journal); //这是最重要的一部,设置日志并启动它
*************************************************************************
******************************日志的初始化******************************
/*
* 这个函数对于jouranl_t结构给出的日志信息,从磁盘块中将日志信息读出来。
* 使用这些读出来的日志信息初始化内存中的一些结构
*/
int journal_load(journal_t *journal)
load_superblock(journal); //使用日志的超级块来初始化整个日志信息。
journal_recover(journal) //应该是将日志上次为完成的完成
journal_reset(journal) //将日志journal请空。
static int load_superblock(journal_t *journal)
journal_get_superblock(journal); //读出超级块。
sb = journal->j_superblock; //这就是从磁盘上读出的超级块的信息。
journal->j_tail_sequence = ntohl(sb->s_sequence);
journal->j_tail = ntohl(sb->s_start); //设置日志的尾块好
journal->j_first = ntohl(sb->s_first); //设置整个日志区域的开始区域
journal->j_last = ntohl(sb->s_maxlen); //结束区域
journal->j_errno = ntohl(sb->s_errno);
************************************************************************
***********************日志的一个transaction的执行过程***********************
设置日志的各个域,当我们系统启动时候,或者开始一个新的日志session的时候,就可使用这个为
下一个session做准备
static int journal_reset (journal_t *journal)
…… //设置日志的各个域
journal_update_superblock(journal, 1); //刷新到磁盘上
lock_journal(journal);
journal_start_thread(journal); //启动整个日志系统。
unlock_journal(journal);
更新日志的超级块到磁盘上,按照wait参数所指示的是否等待磁盘操作完成
void journal_update_superblock(journal_t *journal, int wait)
journal_superblock_t *sb = journal->j_superblock;
struct buffer_head *bh = journal->j_sb_buffer;
…… //设置超级块的属性
sb->s_start = htonl(journal->j_tail); //开始位置就是这一次日志的结束位置
mark_buffer_dirty(bh); //刷新到磁盘上
ll_rw_block(WRITE, 1, &bh);
if (wait)
wait_on_buffer(bh);
*****************************************************************************
********************日志系统的守护进程和日志的提交************************************
全局变量,对于ext3文件系统,只有一个journal,也就是这个变量所指向的日志
journal_t *current_journal;
tid_t log_start_commit (journal_t *journal, transaction_t *transaction)
lock_kernel(); //锁定内核,保护journal->j_running_transaction
……
wake_up(&journal->j_wait_commit); //唤醒提交内核进程,
……
static void journal_start_thread(journal_t *journal)
kernel_thread(kjournald, (void *) journal,CLONE_VM | CLONE_FS | CLONE_FILES); //启动日志守护线程
while (!journal->j_task)
sleep_on(&journal->j_wait_done_commit); //如果没有成功,等待在信号量j_wait_done_commit上
这个就是日志系统的守护内核线程
守护进程有两件事情
1。执行
2。将日志中的一部分数据复制出来写到原来的
int kjournald(void *arg)
journal_t *journal = (journal_t *) arg; //得到这个文件系统的jouranl结构
…… //进行内核线程的一些初始化工作
journal->j_task = current; //设置jouranl的进程域
wake_up(&journal->j_wait_done_commit);
while (1) { //进入无限循环
if (journal->j_flags & JFS_UNMOUNT) //如果文件系统卸载,线程结束
break;
if (journal->j_commit_sequence != journal->j_commit_request) {
if (journal->j_commit_timer_active) {
journal->j_commit_timer_active = 0;
del_timer(journal->j_commit_timer);
}
journal_commit_transaction(journal); //执行日志的commit工作
continue;
}
wake_up(&journal->j_wait_done_commit); //日志完成,唤醒那些等待
//日志提交完成的进程
interruptible_sleep_on(&journal->j_wait_commit);//自己睡眠在这个信号量上
}
…… //后续处理
***************************************************************************
***********************buffer_head和journal_head的关系***********************
这个函数将给定的一个bh加入到一个jh中,并将bh的使用计数加一,从而使系统不释放这个bh
struct journal_head *journal_add_journal_head(struct buffer_head *bh)
spin_lock(&journal_datalist_lock); //加锁
if (buffer_jbd(bh)) { //如果这个bh已经有一个jh和它对应,则不处理
jh = bh2jh(bh);
} else { //否则要信生成一个jh来进行处理
spin_unlock(&journal_datalist_lock);
jh = journal_alloc_journal_head(); //分配jh内存
memset(jh, 0, sizeof(*jh)); //清空内存区域
spin_lock(&journal_datalist_lock);
if (buffer_jbd(bh)) { //由于加锁的问题,要检查一下bh是否
//已经被别的jh加入了
journal_free_journal_head(jh); //如果分配过了,释放原来申请的资源
jh = bh->b_private;
} else {
spin_lock(&jh_splice_lock);
set_bit(BH_JBD, &bh->b_state); //设置bh的状态是BH_JBD
bh->b_private = jh; //将jh加入到bh的private域中
jh->b_bh = bh; //将bh加入到jh的b_bh域中
atomic_inc(&bh->b_count); //将bh的使用计数增加一(!!!非常重要!!!)
spin_unlock(&jh_splice_lock);
}
jh->b_jcount++; //jh的访问计数加一
spin_unlock(&journal_datalist_lock);
return bh->b_private; //返回jh
这个函数分配一个jouranl_head头,用来装载buffer_head
static struct journal_head *journal_alloc_journal_head(void)
ret = kmem_cache_alloc(journal_head_cache, GFP_NOFS); //分配一个journal_head头
*****************************************************************************
************************元数据的写出操作**************************************
这个函数将给定的metadata生成一个新的buffer_head将他加入到transaction中。
这个函数就是ext3文件系统和jbd的接口,也就是所有要将metadata加入到journal中
的接口函数
int journal_write_metadata_buffer(transaction_t *transaction,
struct journal_head *jh_in,
struct journal_head **jh_out,
int blocknr)
if (need_copy_out && !done_copy_out) { //如果需要将metadata写出去
……
}
do {
new_bh = get_unused_buffer_head(0); //分配一个新的buffer_head
} while (!new_bh);
…… //对这个new_bh进行初始化
new_jh = journal_add_journal_head(new_bh); //加入到一个journal_head中
new_jh->b_transaction = NULL; //设置transaction为空,应该是等调用者设置
…… ` //设置new_bh的域以便写入log区
*jh_out = new_jh; //作为参数返回
journal_file_buffer(jh_in, transaction, BJ_Shadow); //久的加入到BJ_Shadow中
journal_file_buffer(new_jh, transaction, BJ_IO); //新的加入到BJ_IO队列中
******************************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -