Xv6文件系统(二):日志层

阅读材料

  • xv6代码:log.c
  • 教材第8章 8.4-8.6节

日志层的作用

  1. 系统崩溃恢复:当系统重启的时候,会通过日志恢复未完成的事务,保证文件系统的一致性
  2. 通过减少磁盘写入次数,提升文件系统性能:日志层可以将多个文件系统操作的修改批量写入日志区域,减少磁盘 I/O 的次数

日志层事务

当进行日志层事务的时候,日志层API调用顺序如下:

begin_op()函数

该函数用于标记一个事务(transaction)的开始,执行的操作如下:

  1. 等待日志系统不处于提交状态
  2. 等待有足够的日志空间来容纳本次调用的写入操作
  3. 如果上述两个条件都通过,则增加outstanding标记这次系统调用
void begin_op(void)
{
	acquire(&log.lock);
	while (1)
	{
		if (log.committing)
		{
			sleep(&log, &log.lock);
		}
		else if (log.lh.n + (log.outstanding + 1) * MAXOPBLOCKS > LOGSIZE)
		{
			// this op might exhaust log space; wait for commit.
			sleep(&log, &log.lock);
		}
		else
		{
			log.outstanding += 1;
			release(&log.lock);
			break;
		}
	}
}

log_write()函数

当对缓存中的数据进行修改后,需要调用本函数来更新日志系统,记录本次修改。该函数功能如下:

  1. 获取日志锁。
  2. 检查日志大小是否超出限制,若超出则引发异常。
  3. 检查事务状态是否合法,若不合法则引发异常。
  4. 遍历日志记录,查找是否存在相同的块号。
  5. 若未找到,则将新块号添加到日志并增加日志长度,同时锁定块。
  6. 释放日志锁。
void log_write(struct buf *b)
{
	int i;

	acquire(&log.lock);
	if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1)
		panic("too big a transaction");
	if (log.outstanding < 1)
		panic("log_write outside of trans");

	for (i = 0; i < log.lh.n; i++)
	{
		if (log.lh.block[i] == b->blockno) // log absorption
			break;
	}
	log.lh.block[i] = b->blockno;
	if (i == log.lh.n)
	{ // Add new block to log?
		bpin(b);
		log.lh.n++;
	}
	release(&log.lock);
}

end_op()函数

该函数用于标记事务的结束,执行的操作如下:

  1. 减少待处理的操作计数 log.outstanding
  2. 若无待处理操作,则设置 do_commit 为 1,并标记正在提交 log.committing
  3. 唤醒可能因等待日志空间而阻塞的 begin_op(),或者因日志正在提交而阻塞的begin_op()
  4. 若 do_commit 为 1,则执行提交操作并重置 log.committing
void end_op(void)
{
	int do_commit = 0;

	acquire(&log.lock);
	log.outstanding -= 1;
	if (log.committing)
		panic("log.committing");
	if (log.outstanding == 0)
	{
		do_commit = 1;
		log.committing = 1;
	}
	else
	{
		// begin_op() may be waiting for log space,
		// and decrementing log.outstanding has decreased
		// the amount of reserved space.
		wakeup(&log);
	}
	release(&log.lock);

	if (do_commit)
	{
		// call commit w/o holding locks, since not allowed
		// to sleep with locks.
		commit();
		acquire(&log.lock);
		log.committing = 0;
		wakeup(&log);
		release(&log.lock);
	}
}

commit()函数

该函数用于提交当前事务,将日志中的修改应用到磁盘数据区域,并清空日志

static void commit()
{
	if (log.lh.n > 0)
	{
		write_log();	  // Write modified blocks from cache to log
		write_head();	  // Write header to disk -- the real commit
		install_trans(0); // Now install writes to home locations
		log.lh.n = 0;
		write_head(); // Erase the transaction from the log
	}
}

日志恢复

initlog()函数

初始化日志结构,读取日志超级块信息,设置日志的起始地址和大小

void initlog(int dev, struct superblock *sb)
{
	if (sizeof(struct logheader) >= BSIZE)
		panic("initlog: too big logheader");

	initlock(&log.lock, "log");
	log.start = sb->logstart;
	log.size = sb->nlog;
	log.dev = dev;
	recover_from_log();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值