深入理解操作系统,学习内核。
正文
从应用程序员的观念转换到系统程序员,软硬件高度耦合。
Booting
Init
kinit1()
- phys page allocator
- 采取链表管理物理页,但实际上不需要使用物理地址,一切都用虚拟地址,在内核address space中,物理地址 = 虚拟地址 - KERNBASE。
- 从 “first address after kernel loaded from ELF file” 开始,对齐,然后分配最多到4MB位置的页。
kvmalloc()
- kernel page table
- 初始化page table,xv6使用tow-level Page Table ,巧妙之处在于先实现一些小函数(walk、mappage)。
- end使用0,非常精彩,因为是unsigned,0 - x等同于max - x + 1
- xv6中没有使用大页,比较简单。
mpinit(); // detect other processors
lapicinit(); // interrupt controller
seginit(); // segment descriptors
picinit(); // disable pic
ioapicinit(); // another interrupt controller
consoleinit(); // console hardware
uartinit(); // serial port
pinit(); // process table
tvinit(); // trap vectors
binit(); // buffer cache
fileinit(); // file table
ideinit(); // disk
startothers(); // start other processors
kinit2(P2V(410241024), P2V(PHYSTOP)); // must come after startothers()
userinit(); // first user process
mpmain(); // finish this processor’s setup
附录
主引导扇区
- 512 Bytes, 结尾两个字节0x55 0xAA。
- BIOS加电后会读取引导扇区,确认有效性,然后转移控制权。
从实模式到保护模式
- 段寄存器,有cs、ss等,每个段寄存器还跟着隐藏的描述符高速缓存器,每次对地址进行转换(其实就是段地址+偏移量),都是直接使用缓存里的值。
- 实模式下,cs里存段地址,没有保护位和界限。(从offset的位数来讲,段空间有一个上限)
- 保护模式下,cs里存段描述符的偏移量,比如0x8,gdt寄存器则存段描述符的起始地址,两者相加得到描述符地址。描述符里记录了段地址、界限和属性。
- 总的来说,描述符的结构非常复杂,知道保护模式的保护来源于此即可,并不是所有人都能修改描述符。
Interrupt Descriptor Table
- IDTR指向IDT,在分页内存下为虚地址。
- CPU会根据异常的编号获取不同的表项,表中存的依然是虚地址,指向handler入口。
- (Xv6)handler并不处理异常,只是将参数入栈,统一跳转到alltraps,由traps函数处理。由于某些异常里,cpu保存的栈不相同(没有error code),所以需要多压栈一个0。
Exception、Interrupt、Traps
- 能够预知的就是Exception,不能预知的是Interrupt。
- Trap通常指用户态到内核态。
分段和分页
- 分段针对的是物理地址,而且出现的原因是历史上,16位寄存器为了兼容20位地址线,从而使用了段寄存器。
- 到寄存器和地址线一样大之后,段寄存器的意义就不大了。在保护模式下增加了额外功能,即段描述符,可以增加一定的权限保护。
- 分页机制出现后,权限保护也可以由Page Table实现,从而分段变得毫无意义,现代操作系统中关闭分段。
CPL、DPL、RPL
- A answer on stackoverflow
- CPL是cs寄存器的后两位。
INT和IRET
- 弄懂INT和IRET两个指令做的事情,就能知道异常是怎么处理的了,只是,INT指令的action和很多标志位相关,不能简单概括。
- 总的来说,这两条指令会负责保存和恢复CS:IP,状态位寄存器,调用IDT。
- 至于其他寄存器,则由软件自行处理,粗暴一点的可以直接pushal。
Trible Interrupt
- Trible Interrupt被认为是error,OS正常运行中不会引发三次以上的Nested Interrupt。
- 从Xv6的代码中,其实看不出来Nested Interrupt有什么影响,Switch函数总是能将最新的context保存在proc结构中。