xv6 lec15 Crash recovery
15.1 File system crash概述
15.2 File system crash示例
- 对于echo在根目录中创建一个新文件x的过程中读写block
15.3 File system logging
- logging可以确保:
- 文件系统的系统调用是原子性的
- 支持快速恢复(fast recovery)
- 原则上可以很高效
- log的执行流程:
- 写文件时,先写write(write log);
- 当写文件操作结束时,在commit写文件操作,也就是在log的某个位置记录属于同一个文件系统的操作的个数(commit log);
- 在log block中存储了所有写block的内容,如果要执行这些操作,直接就是将写在log中的数据写到data区(install log);
- 完成之后,就可以清除log了(clean log),由于log的write ahead rule,能够保证微文件系统调用write等的原子性
- log的header中存储了n和block编号的数组
15.4 log_write函数
- 文件系统的操作以begin_op与end_op来表现一个事务的开始与结束,end_op的时候才会去将数据写入到log
- 在ialloc中已经写了一个inode,也就是将这个inode标记为已用,之后便是写log
- 在写log的时候需要bpin住相关的block
15.5 end_op函数
- end_op函数中中的会执行commit操作,这里是后写log header
- 这里是指先将log拷贝到内存中,在将内存中的文件数据落盘
15.6 File system recovering
- xv6启动之后的文件恢复主要就是
initlog
函数中的recover_from_log
,而recover_from_log
函数主要就是将log中的块复制到磁盘中的正确的位置
15.7 Log写磁盘流程
log write
是之记录一个txn中在log中的写,但是真实的写磁盘(包括写log)是bwrite
15.8 File system challenges
- 对于log的第一个挑战是,在一个事务还有结束的时候,如果将一个block cache中block eviction,也就是写入磁盘,如果中途发生了crash,那么就破坏了事务的原子性,那么
bpin
的作用就是用来保证txn的原子性,bpin
其实就是增加block buffer
的refcnt
,在install_trans
(这个函数用来把log中的block写入到真实磁盘中)会bunpin
block cache中的block cache
- 对于log的第二个挑战是,由于在xv6中只有30个block块,对于
write
系统调用,如果一个写操作的写入的block超过了30,那么就会分成多个小的写操作,每个小的写操作是一个事务,这是由于写操作的语义不是原子的,为什么不是原子的?
- 因为至少还需要log 的header块
- 第三个挑战是,并发的文件系统调用,由于log block只有30个,所以要限制多尔文件系统调用的txn的所有block不能超过30,分开commit会存在问题吗?
- 在begin_op中会检测当前txn是否可以开启,begin_op中通过比较活跃txn个数与最大允许txn的个数,log结构体存在
outstanding
字段记录当前活跃txn,
- 对于阻塞睡眠的开启文件系统系统调用的事务,如果当前log的header的数量与当前正在活跃的txn的可能会使用的log中block超过了一个阈值
LOGSIZE
,那么尝试区开启文件系统调用的进程会sleep在outstanding
(活跃)事务的数目为0时,会去wakeup之前sleep的进程,在wakeup
中只是把sleeping的进程改为runnable,但是如果调度到相关的进程还是会睡眠,因为现在log.committing = 1
,其实这里的outstanding
有些迷惑,outstanding
用来体现活跃txn可能使用的log block(即使不会占用那么多log block),所以如果outstanding
等于0了,就表示这种可能性也没有了