读阿里-操作系统内核开发实战总结

资料来源是之前北邮人论坛下载的ppt.
是由伯松(阿里集团)和王智通(阿里云)做的整理的内部分享。
如作者所言,学习者需要关注的是概念,具体的实现有很多种。因此整理这篇博客,在ppt的基础上再做一些个人层面的总结。

1.内核加载

在一台已经关机的电脑前,我们拥有什么?

a.硬盘,存储着操作系统的内核程序。
b.cpu及寄存器。
c.内存。

概念1:去哪里找内核程序。 bootloader如linux grub去硬盘约定好的地方去读(印象里是第一个分区的第一个扇区?)。
#问题1.去哪里找第一个内核函数?
linux boot protocol. 提前约定好格式或者说是协议。

概念2:内核的第一个c函数调用前的必要条件。
cpu与寄存器的状态,基本内存布局。满足c语言条件即可。
一旦进入c语言,则整个进入了代码可视可控阶段。

2.保护模式基础

内核设计中需要考虑的几个概念:

1.权限控制。2.寻址方式。3.中断处理。4.进程调度。  

概念1:权限控制主要是在寻址的时候做。
页表的bit2 U/S标志位。1是user. 0是sup.
一个程序的地址空间分为两个部分:用户态与内核态。

概念2:页表的生成:
内核的页表在加载内核时已经完成。
用户态的程序在启动时,依赖一些系统调用构建自己的页表。

概念3:线性地址,物理地址。
依靠cr3寄存器,保留着页表自身的位置。

假设一个线性地址:  
31--------22 21------12 11-------0
   pde           pte        offset

概念4:中断/异常。
中断:外部硬件或者软件INT n(软中断?)。
异常:执行过程中探测到错误的指令。

概念5: 中断响应的流程。
“好好的吃着火锅,唱着歌,夯的来了一个中断!”
主要是上下文切换。一部分是cpu在中断时存储/恢复,一部分是中断程序来存储恢复。

两种情况:
1.中断时处于用户态:代码段寄存器,栈指针得留着(SS,ESP)。
另外要进入内核态,用户栈的地址与状态也得留着(EFLAGS/CS/EIP/error Code)。
2.中断时处于内核态:只需要栈切换(EFLAGS/CS/EIP/error Code)

因为中断时,已经保存好了前一个程序的状态,所以中断回来之后,可以发生进程调度。

概念6:进程调度。

1.地址空间:基于页表的寻址保护方式,所以要切回自己的页表。
2.栈:当时执行到哪里停了,接下来继续执行,当时的状态是什么。寄存器+栈就ok了。

问题:内核栈能否单个核只有一个?  
挂在单个程序上的话,就意味着在内核态,这个程序的事情也可以被抢占。
可以参考golang单个p的g0栈。

3.系统调用

概念1:消息传递机制

linux:EAX寄存器为系统调用号
MLXOS: 整个封装成消息体,包含:系统调用号,参数格式,接收者。

概念2:系统调用的过程梳理:

1.按照接口定义将参数准备好。寄存器/栈的分布等
2.调用中断命令。
3.代码段由中断门转换。数据段:先是把当前程序的寄存器等数据入栈,然后切换数据段为内核数据段。
4.内核执行操作。
5.反馈执行的结果。
6.返给用户层。

用户的上下文到底存在哪里: 该程序的内核栈中。
是不是可以这样理解:除了正在用户态运行的程序,
其他的进程都是处在内核的空间内,且内核栈上有自己的上下文。

golang的goroutie我理解状态是存储在本身的栈上,切换时确实时用的g0栈。

用户态调用某一个func:
function(para1) {
   准备好系统调用的参数
   保留现场(各种入栈)
   执行内核的systemcall
   解析内核的返回值,如果需要的话。
   恢复现场
   调用调度程序。(有可能继续执行该程序,也有可能不再执行)
}

4.进程调度

概念1:程序链接与地址分布
形同内核的形式,本质上就是一个定义/协议。ELF。

概念2:进程加载与创建。
硬盘上的代码与内核中的进程数据结构的区别。

解析elf文件:
进程管理数据结构:task_struct.
地址空间管理数据结构 mm_struct.
初始化用户态栈空间。-> 创建一段合法的虚拟地址空间(4KB)+页表注册。
根据程序中每一个程序段,初始化mm_struct。
准备启动的数据结构。
注册到内核进程表。

概念3:进程退出
从内核中创建,从内核中消亡。

概念4:进程切换
重要是两个部分:地址空间切换与栈的切换。

不同程序内核的地址空间是相同的,所以放心的切用户态cr3页目录表。
TLB中非全局项被刷新。   

概念5:休眠与唤醒

从执行队列中将这个进程拿出来,就是休眠。放回去就是唤醒。

依赖数据本身做传递。
1.比如等待某信号为空,让它休眠。之后把信号置位,再让它恢复,它得类似for循环这种。
2.类似函数调用一样。调用了某个函数,需要一个返回值位置,执行的下一条语句地址。把返回值放回去,继续执行就好了。

5.物理内存管理

buddy算法与slab算法。

6.虚拟内存管理

概念1:虚拟内存是在做什么?

内存映射 (mmap)
堆管理 (heap)
栈管理 (stack)
按需分配内存 (pagefault)
地址空间保护(#PF/#GP)
页交换 (swapping)
页缓存 (paging)

概念2: 文件映射与匿名映射

文件映射:磁盘文件–pagecache –进程地址空间 匿名映射:page frame–进程地址空间。

概念3: TLB
转换旁视缓冲。(Translate Lookaside Buffer)