⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 journal.c.txt

📁 linux内核学习笔记 希望想看的人可以很快下载到
💻 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 + -