首页 > 其他分享 >Mit6.s081 Lec14

Mit6.s081 Lec14

时间:2023-07-26 16:15:51浏览次数:38  
标签:Lec14 调用 log Mit6 写入 buffer 磁盘 s081 block

Logging layer

file system 设计的一大重要问题就是 crash recovery。这是因为文件系统操作往往涉及向磁盘多次写入,而几次写入之后的 crash 可能导致磁盘上的文件系统处于一个不一致的状态。

For example, suppose a crash occurs during file truncation (setting the length of a file to zero and freeing its content blocks). Depending on the order of the disk writes, the crash may either leave an inode with a reference to a content block that is marked free, or it may leave an allocated but unreferenced content block.

前者当系统重启之后,可能导致一个磁盘 block 被两个文件所对应,这是一个很严重的问题。

xv6 的解决方法是使用 logging(日志)。xv6 的系统调用不会直接向 on-disk file system data structure(例如 buffer 以及磁盘 block?)写入,而是将它所有希望执行的磁盘写入描述放在磁盘的某个 log 中,once the system call has logged all of its writes, it writes a special commit record to the disk indicating that the log contains a complete operation(这里不知道怎么翻译好了)。此时,系统调用才将 disk write 应用到 on-disk file system data structure。当所有的写入操作完成后,系统调用会清除磁盘上的 log。

如果系统崩溃重启,那么 file-system 代码会在运行进程之前,先检查磁盘上的 log。如果 log 被标记为包含完整的操作,那么 file-system recovery code 会将 log 中的写操作应用到对应的 on-disk file system;否则 file-system recovery code 会直接清除 log,不执行写入操作。

如果 file system 恢复时看到 log 被标记为不包含完整操作,那么说明 log 中的写入操作都还没有应用到磁盘上,即磁盘没有发生真正的写入,因此直接清除 log 即可,不需要执行任何写入操作。

log 使得这些写入操作对 crash 来说是原子的,要么都没写入,要么全都写入到磁盘了。

Log design

log 存在于磁盘的 superblock 中,它由一个 header block 和后面的一系列 updated block copies(更新块的副本,又称 logged blocks) 组成。header block 包含一个扇区号(sector index)的数组,每个 sector index 对应一个 logged block,还包含着 log blocks 的数量。header block 中的计数要么为 $0$,说明 log 中不存在 transaction,要么不为 $0$,说明 log 包含一个完整的已、已提交的 transaction,并包含指定数量的 logged blocks。

xv6 在 transaction 提交时,向 header block 写入数据,在将 logged blocks 复制到 file system 之后,将 header block 的计数置为 $0$。

为了允许 file system 的操作被不同进程并发执行,日志系统(logging system)会将多个系统调用的写操作合并成一个 transaction。因此,单次的 commit 可能涉及多个完整的系统调用的写操作,为了避免一次系统调用被划分成两次 transaction,日志系统只会在没有 file-system 系统调用的时候进行 commit。

一次一起 commit 多个 transaction 的方法被称为 group commit,group commit 能通过平摊一次提交的开销到多次操作上来降低所需的磁盘操作数。

xv6 在磁盘上标识出了一段固定大小的空间用来存放 log。因此,系统调用要写入的 blocks 必须能被这块空间容纳。对 writeunlink 系统调用来说,这个限制可能会导致两个问题:

  1. 假设 write 要写一个大文件,那么可能要写入许多 data blocks、bitmap blocks 和 inode blocks,unlink 一个大文件可能要写入许多 bitmap blocks 和 inode blocks。为了解决这一问题,xv6 的 write 会将一次大的写入划分成多个更小的适合 log 的写入操作,由于 xv6 只使用一个 bitmap lock,因此 unlink 不会造成问题;
  2. 除非能确定系统调用的写入能容纳于 log 的剩余空间中,否则系统不会允许系统调用执行;

Code: logging

系统调用中的 log 的使用一般如下图所示:

rJgukCV9DnRGfaE

begin_op wait until the logging system is not currently commiting, and until there is enough unreserved log space to hold the writes from this call. log.outstanding 会统计预订了 log space 的系统调用的数量,被预订的总空间就是 log.outstanding 乘上 MAXOPBLOCKS。递增 log.outstanding 会预订 log space,并且防止在此系统调用期间发生 commit。 xv6 保守地预计每个系统调用都会写入 MAXOPBLOCKS 个不同的 block。

log_write 表现得就像是 bwrite 的代理那样。它将 block 的 sector number 记录在内存中,在 disk 的 log 部分给这个 block 预留了一个槽位,并调用了 bpin(b) 来保证 buffer b 一定不会被 evict。

调用 log_write 时,调用者已经完成了对 buffer 的数据的修改;

调用 bpin(b) 之后,哪怕 buffer 缓存不足时,buffer b 也不会被换出,它修改了 buffer b 的 refcnt,之所以要这样设置,是因为假设 buffer b 中途被换出到磁盘,就违背了“必须 commit 之后再将写入应用到磁盘”的设定了,也就破坏了磁盘写入相对于 crash 的原子性。

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);
}

在 commit 之前,block 必须要缓存在 buffer 上, 这是因为 buffer 唯一地记录着我们对这个 block 的修改;只有在 commit 之后,才可以将 buffer 写入磁盘上的位置;同一 transaction 的假设要读取这个 block,那么它必须能看到对这个 buffer 的修改。log_write 会注意到一个 transaction 中,多次写入同一个 block 的情况,如果是写入同一个 block,那么就不会添加新 block 到 log 中,而是在 log 中为该 block 分配相同的槽位。这种优化通常被称为 absorption(合并),这样的话,从 buffer 写入到 log 的槽位只会发生一次,即使写入 buffer 发生了很多次。

end_op 首先会减少未完成的系统调用的计数(说明有系统调用完成了),如果计数被递减到了 $0$,它就会通过调用 commit() 来提交当前的 transaction。这个过程分为四个阶段:

  1. write_log 将每个在 transaction 被修改的 block 的 buffer 复制到 log 对应的槽位中(先复制到了 log 的 buffer 的对应的槽位中,然后将这个 log 的 buffer 写入到磁盘)
  2. write_head 将 header block 从 buffer 写到磁盘中,而这就是 commit 的关键节点:
    • 当完成 header block 的从 buffer 写入到磁盘后,如果发生了 crash,那么恢复程序会从 log 中执行该次事务的写操作,将数据从 log 的 buffer 写入到磁盘;
  3. install_trans 会从 log 的 buffer 中读取每个 block,然后写入到文件系统(磁盘);
  4. end_op 将 log header 的计数重置为 $0$(说明 log 中不存在 transaction),这会在下一个 transaction 要写入 logged blocks 之前完成,这保证了 crash 恢复时,不会将前一个 transaction 的 header 与下一个 transaction 的 logged blocks 混在一起恢复,即保证了一次恢复不会涉及两个 transaction。

recover_from_log 会被 inilog 调用,而 initlog 会被 fsinit 调用,这个调用的时间点在第一个用户进程运行之前,即第一个用户进程运行之间,文件系统会进行自检。它会读取 log header,如果 log 包含一次已提交的 transaction,那么它会模拟 end_op 的操作。

使用 log 的例子可以参照 kernel/file.c 中的 filewrite,注意 writei 会调用 write_log

begin_op();
ilock(f->ip);
if ((r = writei(f->ip, 1, addr + i, f->off, n1)) > 0)
    f->off += r;
iunlock(f->ip);
end_op();

标签:Lec14,调用,log,Mit6,写入,buffer,磁盘,s081,block
From: https://www.cnblogs.com/zwyyy456/p/17582719.html

相关文章

  • MIT 6.S081 Lec13: File system
    Overview文件系统的设计目标就是组织和存储数据,文件系统一个比较重要功能是持久化,即重启之后,数据不会丢失。xv6通过把数据存储在virtiodisk上来实现持久化。文件系统设计的几大挑战:Thefilesystemneedson-diskdatastructurestorepresentthetreeofnameddirecto......
  • MIT 6.S081 Thread switching
    Multiplexingxv6通过将cpu从一个进程切换到另一个进程来实现multiplex(多路复用),进程的切换会在两种情形下发生:xv6的sleep与wakeup机制在进程等待IO完成或者等待子进程退出又或者在sleep系统调用中等待的时候切换进程。xv6会周期性地强制切换进程,从而应对那些长时......
  • MIT 6.S081 Multiprocessors and locking
    whylock防止多核并行运行下的racecondition导致的错误。内核中的数据是典型的concurrently-accessed的数据。raceconditionandhowthelockavoiditAraceconditionisasituationinwhichamemorylocationisaccessedconcurrently,andatleastoneaccess......
  • MIT6.s081/6.828 lectrue1:Introduction and examples
    目前课程官网能够查到2020,2021.2022秋季的课程表,但是视频都是2020年录制的那一版简单复习+回顾下自己的OS学习之旅参考资料:官网:https://pdos.csail.mit.edu/6.828/2022/schedule.html视频翻译:https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081/教材英文......
  • MIT6.S081学习笔记--lec 1
    引言操作系统的目标abstractH/W抽象化硬件multiplex多路复用isolation隔离性sharing共享(进程通信,数据共享)security/accesscontrol安全性/权限控制performance性能/内核开销rangeofapplications多应用场景操作系统概览操作系统应该提供的功能:1.多进程支......
  • MIT 6.S081 Isolation & System call entry/exit
    Trap机制程序运行往往需要完成用户空间和内核空间的切换,每当:程序执行系统调用(systemcall);程序出现了pagefault等错误;一个设备触发了中断;都会发生这样的切换。这里用户空间切换到内核空间通常被称为trap,因此有时候我们会说程序“陷入”到内核态。trap机制需要尽可能......
  • MIT 6.S081 操作系统组织架构
    进程概述64位的RISC-V的VAS是39位的,即VA只有39位,而Xv6则只有38位,最大虚拟地址为#defineMAXVA0x3fffffffff。VAS的顶端,即最高位存放了两个page,一个是用于trampoline,一个用于mappingtheprocess'strapframe。Xv6使用这两个page来切换到内核以及返回。......
  • MIT 6.S081 页表
    Paginghardware总的来说,Xv6的虚拟内存到物理内存的映射方式与x64是一致的,都是使用页表来进行映射。区别在于,Xv6只使用了三级页表,而x64则是使用四级页表,另外,二者的页表层级的命名也有区别,对Xv6来说,最高级的页表是L3(其地址存放于寄存器satp中)。每个pagetable含有5......
  • MIT6.5840 lab2,3 记录
    参考链接课程地址如何Debug:没有它可怎么活,几万行的日志怎么看Students'GuidetoRaftraft算法可视化:直观展示raft可视化简单入门raft讲解视频:强烈推荐感想感觉理论+实践来学一个东西才学的深刻,特别是对于我这样对抽象理解不太行的,每次见识了一个算法或系统真正如何运行......
  • MIT 6.s081 实验环境搭建
    准备工作Linux系统,我是在实验室配的主机上装了DebianBookworm,然后mac通过ssh连接上去进行操作,宿舍里则是使用的wsl2,里面的发行版也是DebianBookworm。开始配置clone源码在~/Documents/code/mit目录下执行gitclonegit://g.csail.mit.edu/xv6-labs-2021,将源码cl......