category
type
status
date
slug
summary
tags
password
Property
Mar 15, 2026 03:14 PM
icon

Lab1『🎉』

脑溢血的vscode函数定义跳转配置

配置gdb调试环境

pingpong

  1. 管道特性的思考
只要管道还有至少一个写端未被关闭,即使当前管道为空,读进程也会认为未来可能有数据写入,于是选择阻塞而非放弃。

1. API 设计约定

在 Unix/Linux 系统中,pipe() 系统调用的设计规范就是:
  • pipefd[0]读端(read end)
  • pipefd[1]写端(write end)
这个约定源于早期的 Unix 设计,已经成为 POSIX 标准的一部分。

2. 记忆方法

  • 0 像字母 "O"(代表 "Output from pipe" 或 "Open for reading")
  • 1 像数字 "1"(代表 "Input to pipe" 或 "One-way in")
更直观的记忆:
  • 0 = stdin(标准输入,用于读)
  • 1 = stdout(标准输出,用于写)

primes实验,管道在fork链中的开关情况

如果进程链是 p1 → p2 → p3(即 p1 创建 p2,p2 创建 p3),管道在 p1 中创建,随后 p2 关闭了写端,那么 p3 不会拥有该管道的写端文件描述符

详细分析:

  1. 初始状态
    1. p1 调用 pipe(fd),获得两个打开的文件描述符:fd[0](读端)和 fd[1](写端)。
  1. p1 fork 出 p2
    1. p2 继承 p1 的文件描述符副本,因此 p2 也拥有 fd[0] 和 fd[1],两者都处于打开状态。
  1. p2 关闭写端
    1. p2 调用 close(fd[1]),关闭自己持有的写端文件描述符。此时 p2 的文件描述符表中,写端已关闭,但读端 fd[0] 仍打开。
  1. p2 fork 出 p3
    1. p3 继承的是 p2 当前的文件描述符表副本。由于 p2 已关闭写端,p3 只会继承到打开的读端 fd[0],而不会继承写端 fd[1](因为它在 p2 的文件描述符表中已关闭)。

关键点:

  • 文件描述符的继承是复制当前状态:子进程获得的是父进程调用 fork() 时文件描述符表的快照。
  • 管道的写端是否仍然存在:虽然 p3 自己没有写端,但管道的写端可能仍在其他进程中打开(例如 p1 可能仍未关闭写端)。不过,p3 自身无法进行写入操作,因为它没有写端的文件描述符。
  • 管道生命周期:只要至少有一个写端保持打开(无论在哪个进程),管道的读端就不会看到 EOF。如果 p1 也关闭了写端,那么当所有写端关闭后,读端在读取完剩余数据后会收到 EOF。

结论:

p3 从 p2 继承的文件描述符中没有写端,因此 p3 拥有的管道写端处于关闭状态
本实验代码实现
  1. fork系统调用
notion image

xargs

此实验涉及到Linux命令xargs的相关知识,建议先去学习一下这个命令的工作原理再做实验,否则寸步难行

实验结果

notion image

Lab2『🚀』

Perl脚本文件

主要用于自动生成系统调用存根代码

编写并添加新系统调用的步骤

用户空间
  • 在user.h中声明系统调用跳板
    • 在usys.pl写明系统调用接口
      内核空间
      1. 写明系统调用号
        1. 在syscall.c全局声明系统调用,并将其与系统调用号关联

        跟踪系统调用的核心逻辑

        trace是父进程,exec执行被跟踪的系统调用子进程,子进程通过在fork时复制父进程的跟踪掩码,然后在执行系统调用时通过比对掩码和系统调用号来确定当前系统调用是否被跟踪。
         
         
        notion image

        Lab3 『important⚡⚡』

        要明确一个前提:本项目实现的是一个os,而底层的如MMU「Memory Management Unit」由RISC-V架构提供的硬件自动完成虚拟地址到物理地址的转换,Sv39模式,通过在satp寄存器中设置根页表的物理地址和对应页表中的页表项即可。 这正是copyin_new和copyin的区别,copyin_new采用时『在lab3中的task3完成后』已经将用户空间的数据映射到用户的内核空间页表中,进而可以在内核空间中直接通过使用用户空间的虚拟地址来获取对应的数据。而copyin不行,故只能通过软件模拟页面的访问来在内核空间获取用户空间对应虚拟地址中的数据。
        这是内核空间的虚拟地址分布,真正能使用的物理内存空间为KERNBASE到PHYSTOP,kinit()初始化该部分挂到freelist中
栈页的低一页用作guard-page不进行映射,用于捕捉stack overflow
        这是内核空间的虚拟地址分布,真正能使用的物理内存空间为KERNBASE到PHYSTOP,kinit()初始化该部分挂到freelist中 栈页的低一页用作guard-page不进行映射,用于捕捉stack overflow
        这是用户空间的虚拟地址分布
        这是用户空间的虚拟地址分布
        kernel/vm.c xv6的虚拟内存机制,难点,高频提问点 准备工作:阅读xv6第三章pdos.csail.mit.edu,翻译:第三章 页表 · 6.S081 All-In-One,阅读kernel/vm.c源码
        术语解释
        1. Page Table Entries/PTE:页表条目
        1. Physical Page Number/PPN 物理页码:44bit「页框号44位」
          1. virtual address的高25位不使用,只使用低39位,其中中27位作为虚页号
            notion image
            RISC-V使用的是三级页表结构
            notion image
            satp寄存器存储根页表的物理地址,每个CPU有一个satp
         

        lab-3/task2

        • 内核获取user space 的数据只能通过将对应虚拟地址转换为对应物理地址才可以使用
        前置知识:
        『在进行此实验前的系统功能』xv6中进程在用户态下都有各自的用户态页表,但是通过系统调用进入内核态时要切换到使用内核态下的页表,这个内核态的页表是全局共享的。 架构缺点:
        1. 进程可能会意外或者恶意的访问其他进程的内核数据,会影响系统中其他进程的稳定性
        1. 每次创建和删除进程是都需要小心更新共享的页表条目,确保不同进程之间内存不会冲突或被错误覆盖,无疑增加系统的复杂性和开销
        1. xv6中虚拟地址,在用户空间和内核空间中的不同使用情况:
          1. 空间类型
            使用地址类型
            原因
            用户空间
            虚拟地址
            通过页表转换,实现隔离和保护
            内核空间
            虚拟地址
            采用直接映射策略
            虚拟地址(Virtual Address) :
            • 定义 :程序代码中使用的地址
            • 特点 :程序看到的是连续的、独立的地址空间
            • 范围 :0 到 2^39 - 1(约 512GB,Sv39 模式)
            • 隔离性 :每个进程有独立的虚拟地址空间
            物理地址(Physical Address) :
            • 定义 :实际硬件内存的地址
            • 特点 :真实的内存地址,直接对应硬件内存单元
            • 范围 :0 到 实际物理内存大小(如 128MB)
            • 共享性 :所有进程共享物理内存
         
        进程切换的每个进程的页表切换机制⚡⚡:
        实验目的:
        确保每个进程进入内核态之后,都有自己独立的内核页表
         
         
        实验大体流程
        1. 修改vm.c中kv相关程序,以供进程的内核页面的使用
        1. 将每个进程内核栈的预分配推迟到进程创建时创建进程的内核页表后再分配
        1. 在进程的调度scheduler()中实现在进程被调度前设置satp寄存器的内容为要调度进程的内核页表物理地址『因为此时待调度进程处于内核态』
        1. 在进程结束时先回收分配给该进程的内核栈,然后回收分配给其的内核页面。『freeproc(), proc.c』
        实验结果
        notion image

        lab-3/task3

        实验目的:将进程在用户空间的页表信息映射到内核空间的页表种。 👾 attention! : 在本实验中用户空间在内核中的虚拟地址空间范围只有[0,PLIC)

        task3的重要前置知识

        exec()实现了将程序加载到当前被调度进程中以便使该进程能够执行该程序,由于进程在allocproc中创建时并没有绑定任何程序,故在exec中绑定相关程序后要释放旧的页面并设置新的页面。

        思路:

        1. 先找到涉及进程用户页面修改的地方『exec,fork,growproc,userinit』,利用映射来达到进程的内核页面进行同步修改
        1. 细节处理:由于进程在内核空间中虚拟地址范围是[0,PLIC),故需要在exec中为进程加载程序时要注意处理进程的大小『针对虚拟地址来说』不要超过PLIC『不需要考虑CLINT,因为此地址只在内核启动时使用』
        实验结果
        notion image
         

        本task遇到的问题

        remap
        无语了。。。把np→axisz_kernel_pagetable写成了p→axisz_kernel_pagetable
        无语了。。。把np→axisz_kernel_pagetable写成了p→axisz_kernel_pagetable
        notion image
         

        score

        notion image

        实验后疑问🤯🫨

        • lab3 的task2 done之后全局内核页表除了在系统启动时使用之外还会再被使用吗?

        Lab 4 『🐌✨』

        前置知识

        trampoline中代码是同时在用户和内核空间中映射的。
        trap的三种不同情况
        traps from user space, traps from kernel space, and timer interrupts
        涉及到的registers
        1. stevc:内核在此写入要进行陷入处理的程序的地址『supervisor trap vector base address register』
        1. sepc:从陷入返回,保存pc供sret『中断返回指令』在陷入返回时将pc设置为sepc中的值。『supervisor exception program counter』
        1. scause: The RISC-V puts a number here that describes the reason for the trap.
        1. sscratch:内核在这里放一个值,该值在trap handling一开始就被使用 『supervisor scratch register』
        1. sstatus:
          1. SIE:控制设备中断是否启用,如果内核清空SIE,RISC-V将推迟设备中断 SPP:指示trap来源『用户/内核』,进而控制sret的返回模式
        从user space 陷入逻辑

        task 1『Q&A』

        1. Which registers contain arguments to functions? For example, which register holds 13 in main's call to printf?
          1. a0~a7; a2
            li a1,12 12是f(8)+1的值,编译器进行了优化,直接计算出结果 auipc a0,0x0 功能:将20位立即数0x0左移动12位后与当前PC值相加,结果存入目标寄存器a0. auipc: add upper immediate to PC
        1. Where is the call to function f in the assembly code for main? Where is the call to g? (Hint: the compiler may inline functions.)
          1. At what address is the function printf located?
            1. ra=30H, 故printf地址为30H+600H=630H
          1. What value is in the register ra just after the jalr to printf in main?
            1. jalr指令的标准格式:jalr rd, offset(rs1) ,跳转到rs1+offset的位置,并将jalr指令的下一条指令的位置存储在rd
              此处rd被省略了,rard,故ra=0x34+4=0x38
          1. Run the following code:
            1. What is the output? Here's an ASCII table that maps bytes to characters.
              The output depends on the fact that RISC-V is little-endian. If RISC-V were instead big-endian, what would you set i to in order to yield the same output? Would you need to change 57616 to a different value?
              output: Hello World reason: 57616 = 0xello, 0x64=’d’, 0x6c=’l’, 0x72=’r’ 陷阱😒: 无论采用大端还是小端,57616的16进制格式化输出都是0xello, 大端只会影响i的转换,要想得到同样的输出i改为:0x726c6400
          1. In the following code, what is going to be printed after 'y='? (note: the answer is not a specific value.) Why does this happen?
            1. y=的值取决于寄存器a2中存放的值

          task 2 『backtrace』

          stack layout fp 是fram pointer,即栈帧的地址,栈的地址是从高往低处生长的。 调用过程中的每个函数的栈的分配也是从高地址到低地址进行的。
          notion image

          task 3 『use trap implement cron job』

          • core code

          实验结果:

          notion image

          Lab 5 『🤠』

          前置知识

          涉及两个重要的寄存器:scause『加载、存储、访问指令时的缺页错误,引发trap』、stval『记录导致trap的虚拟地址』 scause中:13 load引起的page fault,15 store引起的page fault,12 执行指令引起的page fault

          lab 原理

          将sbrk()改为只对进程分配内存需求的标记,真正的物理内存的分配则在page fault被触发的时候才被分配。『在usesrtrap中实现真正的物理内存分配』

          实现堆空间的惰性分配

          核心代码
          实现惰性分配后成功执行echo hi
          notion image

          task3

          核心代码
          copyin/copyout添加惰性分配处理
          实验结果
          notion image
          notion image

          Lab 6 『todo』

           
          MySQL的事务隔离机制深度学习(入门/入土?)
          Loading...