xv6 想到什么记什么
xv6不同视频里安装了不同版本 两个我都装了
一个是xv6-public 点进去是深大视频里配套的i386 arch
另一个是xv6 risc-V 就是另一个我参考的视频里
运行xv6 用的QEMU 是个linux上的模拟器 xv6又是个unix变体简易版系统
所以最终说起来这个系统是一个程序,跑在一个模拟器程序里,模拟器程序跑在虚拟机里,虚拟机跑在我的window系统里,还挺绕
那么准备工作就有:
1.装个18.04以上的ubuntu 我的版本是20.04
2.装QEMU 这里有riscv的跟i386的 我装了俩
https://zhuanlan.zhihu.com/p/267159664
https://blog.csdn.net/icekittenice/article/details/102596537
https://blog.csdn.net/m0_45291976/article/details/109121959
3.拉取xv6 从github 两个都拉取了 一个public 一个riscv
拉取时git https的问题就不说了 最后都容易443,解决办法是在github.com之前加上gitclone.com 比如https://gitclone.com/github.com/xxx/xxx.git
https://blog.csdn.net/weixin_51015707/article/details/121557471
4.安装
拉取完了之后按教程make make qemu就行
当然这部分前提是有编译工具链 也就是riscv那一套 在上面装QEMU的时候有链接里有
risc-v工具链安装如果按照官网教程来安装,可能会因为下载过慢而失败,教程中的安装命令是brew install riscv-tools,但riscv-tools由几个部分组成,我们其实只需要riscv-gnu-toolchain,源码下载方式如下:
git clone --recursive https://github.com/riscv/riscv-gnu-toolchain
下载完成之后输入如下命令测试一下:
riscv64-unknown-elf-gcc -v
如果能显示版本信息则说明安装成功
如果要退出,先按Ctrl+a,松手后再按x
最后讲一下QEMU的调试技巧 有个不完全指北的网站可以参考 并且结合b站一个视频去做GDB的调试练习以及vscode
https://www.cnblogs.com/KatyuMarisaBlog/p/13727565.html 不完全指北
这里头有一个找gdbinit的问题 用Ctrl+H查看ubuntu fs中的隐藏文件
另一个问题是gdb的路径问题 不用riscv的gdb 用默认的就好
如何GDB调试QEMU 用到GDBStub
https://blog.csdn.net/Gefangenes/article/details/131193235?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0-131193235-blog-83758127.235^v39^pc_relevant_default_base&spm=1001.2101.3001.4242.1&utm_relevant_index=3
重点在vscode 因为现代编辑器嘛
方法是make qemu-gdb 开启gdb
然后vscode里修改launch.json 远程地址选GDB服务器
"miDebuggerServerAddress"
https://www.cnblogs.com/tocy/p/gnu-binutils-simple-usage.html
https://zhuanlan.zhihu.com/p/645286863 bios MBR boot

这页PPT提到了进程的内核栈与用户栈
之前的学习中我好像只关注了进程在用户空间的栈 而没有注意过内核栈的情况,在此进行补充学习:
https://blog.csdn.net/jasonLee_lijiaqi/article/details/80181501
https://zhuanlan.zhihu.com/p/605736082
XV6启动过程

分成主处理器的启动和其他处理器的启动 原因?
主处理器加电之后运行BIOS,从启动盘中读启动扇区的代码 把kernel代码加载进来并执行。
BIOS会把启动扇区装到指定地址位置
为了软件兼容可以工作在16位实模式,再转换为32位保护模式设置堆栈(防止c覆盖了)(分割内核态和用户态分段)。

启动扇区文件由bootasm.S以及bootmain.c 最后编译生成的512字节的文件
2个端口 0x60,0x64
驱动中把 0x60 叫数据端口
驱动中把 0x64 叫命令端口
端口0x64是键盘控制器的IO端口 .
键盘控制器有两个端口0x64和0x60 .
端口0x64(命令端口)用于向键盘控制器(PS / 2)发送命令 .
端口0x60(数据端口)用于向/从PS / 2(键盘)控制器或PS / 2设备本身发送数据 .
这里的代码使CPU轮询PS / 2键盘控制器以查看它是否正忙 .
inb $0x64,%al # Get status
上述指令读取PS / 2控制器的状态寄存器,其长度为8位 . 具体来说,它试图读取输入缓冲区的状态 - 位1 .
testb $0x2,%al # Busy?
在此处测试从前一个inb指令返回的字节 . 这里检查输入缓冲区的状态以查看它是满还是空 - 位1(0x2) .
Status Register - PS/2 Controller
Bit Meaning
0 Output buffer status (0 = empty, 1 = full) (must be set before attempting to read data from IO port 0x60)
1 Input buffer status (0 = empty, 1 = full) (must be clear before attempting to write data to IO port 0x60 or IO port 0x64)
2 System Flag - Meant to be cleared on reset and set by firmware (via. PS/2 Controller Configuration Byte) if the system passes self tests (POST)
3 Command/data (0 = data written to input buffer is data for PS/2 device, 1 = data written to input buffer is data for PS/2 controller command)
4 Unknown (chipset specific) - May be "keyboard lock" (more likely unused on modern systems)
5 Unknown (chipset specific) - May be "receive time-out" or "second PS/2 port output buffer full"
6 Time-out error (0 = no error, 1 = time-out error)
7 Parity error (0 = no error, 1 = parity error)

重点关注汇编中去执行函数的方法

这页PPT将的是从线性地址(虚拟地址)到物理地址在存在两级页表情况下的转换过程
第一级页表是的起始位置放在CR3寄存器里头,靠pgd_offset找到当前所在的pde_t,结合第二个pd_offset就能找到pde_t这就是要找的线性地址位于的真实物理页还差一个页内偏移去加上就能得到最终的物理地址,这个分页机制要理解
内存中每4KB做一个页帧数

这样每次发一个地址出来 比如图例那个地址 可以分成PD PT offset 10bit 10bit 12bit 一共32bit 8bytes 会转换为3 2 5 这几个数
然后CR3告诉我 本进程的页表在哪里 在第48个页帧 取第三项 找到101 取第二项 502 找到502页帧 加上偏移 里面的地址就是真实地址


选择子 查表 加上偏移 得到线性地址 线性地址通过页表转换成物理地址
这里页表内PPN不需要用完整的32位地址去表示 因为它是对其的,所以2^20就已经是1M范围,低12位可以拿来做标志位
代码里 CR4开启大页模式 CR3存页表地址 CR0里头去控制分页 启动分页
最后设置堆栈指针
xv6支持两种ISA架构 x86以及RISC-V
x86的架构下 寄存器分类

x86寄存器知多少:https://zhuanlan.zhihu.com/p/565060715
https://blog.csdn.net/qq_29328443/article/details/107188689
32位CPU所含有的寄存器有:
- 4个数据寄存器(EAX、EBX、ECX和EDX)
- 2个变址寄存器(ESI和EDI) 和2个指针寄存器(ESP和EBP)
- 6个段寄存器(ES、CS、SS、DS、FS和GS)上面图里没有
- 1个指令指针寄存器(EIP) 1个标志寄存器(EFlags)
- 9个控制寄存器(C0、C1、C2、C3、C4、C5、C6、C7、C8)其中C5~C7架构保留。
- 3保护模式寄存器(GDTR,LDTR,IDTR)
32bit与64bit存在很多差异
上面图里差异只给了名字,而实际上
- 64位有16个寄存器,32位只有8个。
- 但是32位使用e开头(extended),而64位前8个使用了r开始。
- 64bit的寄存器名增加额外8个(r8 - r15),其低位分别用d,w,b指定长度。
- 32位使用栈帧来作为传递的参数的保存位置; 而64位使用寄存器,分别用rdi,rsi,rdx,rcx,r8,r9作为第1-6个参数。rax作为返回值
- 32位用ebp作为栈帧指针,64位取消了这个设定,没有栈帧的指针, rbp作为通用寄存器使用
- 64位支持一些形式的以PC相关的寻址,而32位只有在jmp的时候才会用到这种寻址方式。
数据寄存器
数据寄存器主要用来保存操作数和运算结果等信息,从而节省读取操作数所需占用总线和访问存储器的时间。
32位CPU有4个32位的通用寄存器EAX、EBX、ECX和EDX。这里说的和网上不一样 有的说32位有8个通用寄存器EAX,EBX,ECX,EDX,EBP,ESP,ESI,EDI功能和上面差不多
对低16位数据的存取,不会影响高16位的数据。这些低16位寄存器分别命名为:AX、BX、CX和DX,它和先前的CPU中的寄存器相一致。 4个16位寄存器又可分割成8个独立的8位寄存器(AX:AH-AL、BX:BH-BL、CX:CH-CL、DX:DH-DL),每个寄存器都有自己的名称,可独立存取。64位 32位 16位的高低位包含关系
AX和AL通常称为累加器(Accumulator):可用于乘、除、输入/输出等操作(在乘除指令中指定用来存放操作数)
BX称为基地址寄存器(Base Register):在计算存储器地址时,可作为基址寄存器使用。
CX称为计数寄存器(Count Register):用来保存计数值,如在移位指令、循环指令和串处理指令中用作隐含的计数器(当移多位时,要用CL来指明移位的位数)。
DX称为数据寄存器(Data Register):在进行乘、除运算时,它可作为默认的操作数参与运算,也可用于存放I/O的端口地址。DX在作双字长运算时,可把DX和AX组合在一起存放一个双字长数,DX用来存放高16位数据。
32位CPU中,其32位寄存器EAX、EBX、ECX和EDX不仅可传送数据、暂存数据保存算术逻辑运算结果,而且也可作为指针寄存器,所以,这些32位寄存器更具有通用性。
浙公网安备 33010602011771号