liunx0.11参考自《linux内核设计的艺术》
启动过程
- 启动BIOS,准备实模式下的中断向量表和中断服务程序
- 利用中断服务程序从启动盘加载操作系统到内存中
- 转换到32位保护模式
-
加电自检
-
进入16位实模式运行,并强行将CS:IP设置为0xF000:0xFFF0(BIOS起始地址)
-
执行内存地址0xFFFF0的BIOS代码,进行POST系统自检(计算机软硬件的测试)
-
- intel将所有的80x86系列的CPU,都设计为加电即进入16位实模式运行,并强行将CS:IP(CPU将要执行内存程序地址)设置为0xF000:0xFFF0,即0xFFFF0
- CPU的逻辑电路只能执行内存中的程序,任何程序(包括操作系统)加载到内存RAM中才能执行
- CS寄存器:存放内存代码段区域的段基址
- IP寄存器:存储将要执行的下一条指令的偏移地址
-
BIOS程序构造内存
-
0x00000-0x003FF:1kb的内存存储中断向量表
-
0x00400-0x004FF:相邻的256字节构建BIOS的数据区
-
在相隔57KB后的位置0x0E05B处加载8KB左右的中断服务程序
-
执行
int 0x19
中断,将磁盘的第一个扇区(0盘面0磁道1扇区)加载到内存0x7c00处(BOOTSEG)处,然后复制到0x90000(INITSEG)处
-
- 中断向量表中含有256个中断向量,每个中断向量包含4个字节(两个字节表示CS,两个字节表示IP)
- 每个中断向量对应一个中断服务程序
-
执行到
jmpi go,INITSEG
后,CPU跳转到0x90000处开始执行(复制的原因是规划内存的使用) -
执行
int 0x13
中断(将指定扇区的代码加载到内存的执行位置)- 将磁盘2~6扇区的setup.s程序加载到内存的SETUPSEG(0x90200)处
- 将240个扇区的system模块加载到内存的SYSSEG(0x10000)处后面的120KB空间中
-
执行
jmpi 0,SETUPSEG
后,跳转至0x90200处开始执行setup程序 -
setup程序
-
将机器系统数据复制到0x90000~0x901FD,共510字节,即原来的bootsect只有两个字节未被覆盖
-
关中断,将system模块移动到内存地址的起始位置0x00000,覆盖了BIOS的中断服务(破旧立新)
-
初始化中断描述符表寄存器IDTR和全局描述符表寄存器GDTR
-
将CR0寄存器的第0位(PE)置1,即设定处理器的工作方式为保护模式
-
- 0x90000处bootsect程序刚执行完,执行的0x90200处的setup程序,即立刻将其使用的内存覆盖。这种安全高效的内存空间利用,虽然与当时的硬件条件有关,但是仍然值得学习
- 关中断是指将CPU的标志寄存器中的中断允许标志IF置为0,汇编指令关中断cli和开中断sti通常配合使用,保证一个过程的完整执行
- 全局描述符表GDT是系统中存放段描述符的数组,进行保护模式下的段寻址。GDTR存储GDT基地址
- 中断描述符表IDT保存保护模式下所有中断服务程序的入口地址。IDTR存储IDT基地址
jmpi 0,8
中8表示二进制1000,其中最后两位00表示内核特权级,11表示用户特权级,第三位0表示GDT,1表示LDT,最高位表示所选LDT/GDT的表项
- system模块由head程序和内核程序组成,执行head程序。
- 在内存空间创建内核分页机制,即在0x000000的位置创建页目录表、页表、缓冲区、GDT、IDT,并将已经执行的代码覆盖,即将自己废弃
- 将寄存器CS、DS、ES、FS和GS等从实模式转换到保护模式
- 初始化中断描述符表IDT和GDT
- 验证A20地址是都打开,如果没打开计算机仍然处于20位寻址模式
- 创建分页机制,初始化页目录表和4个页表放在物理内存的起始位置,一共5页(每页4KB)
- 对页目录表基址寄存器CR3进行初始化,将CR0寄存器最高位置1表示打开分页机制
- 在最后调用setup_paging函数时,利用类似栈溢出攻击的原理,将main函数地址压栈到函数栈的返回地址进行main函数的执行
设备环境初始化及激活进程0
- 多进程是指各个用户进程在运行过程中彼此不互相干扰,而进程边界的划分是利用系统的进程管理数据结构实现的,主要包括:task_struct、task[64]、GDT
- task_struct:是每个进程所独有的数据结构,标识进程的各种属性值,包括剩余时间片、进程执行状态、局部数据描述符表LDT和任务状态描述符表TSS
- task[64]:存储着系统中所有进程的task_struct指针
- GDT:存储着一套针对所有进程的索引结构,通过索引项操作系统可以间接的与每个进程中的LDT和TSS建立关系
设备初始化
- 设置根设备和硬盘
- 用机器系统数据中内存地址为0x901FC的设备号进行根设备设置
- 用机器系统数据中起始自0x90080的32字节的硬盘参数表初始化内核中的硬盘信息drive_info
- 规划物理内存
- 根据实际物理内存大小动态设置主内存区、缓冲区和虚拟盘空间大小
- 计算机中的运算需要CPU和内存的相互配合,CPU进行规则运算,而内存负责数据存储。实际除了内核代码和数据所占用的内存空间,其余物理内存主要分为
- 主内存区:进程代码运行的空间,即进程管理数据结构运行的地方
- 缓冲区:主机与外设进行数据交互的缓冲区
- 虚拟盘区:可选,能够与外设进行数据交换,提高系统执行效率
- 初始化虚拟盘空间
- 调用rd_init()函数,将虚拟盘区的请求项处理函数do_rd_request()与请求项控制数据结构blk_dev的第二项挂接,挂接后虚拟盘所在的内存区域全部初始化为0,内核能供通过调用do_rd_request函数处理与虚拟盘请求项相关的操作
- 初始化主内存区
- 调用
mem_init(main_memory_start,memory_end)
,先将所有内存区域的页面设置使用次数为USED(100,即已使用),然后将主内存中的所有页面使用计数全部清零(系统只将使用计数为0的页面作为空闲页面)
- 调用
- linux设计者对于内核和用户进程使用两种不同的分页管理方法,目的是用户进程无法通过线性地址推算具体的物理地址,而让内核能够访问用户进程,用户进程不能访问其他的用户进程,更不能访问内核
- 内核的分页管理使用逻辑地址与物理地址的直接映射
- 用户进程的分页管理逻辑地址和物理地址差距很大,无法进行具体物理地址的推演,不能访问其他用户进程空间,更不能访问内核
- 使用trap_init()函数将中断、异常处理的服务程序和IDT进行挂接,逐步建立中断服务体系
- CPU中断的地址关系
- IDTR寄存器存储内存中断描述符表IDT的首地址
- IDT是一个中断描述符数组,存储各种中断数据结构,包含中断属性和其中断服务程序的偏移地址
- 中断服务程序调用可以让CPU暂停当前工作,转去执行完中断服务后,再继续执行原来的程序
- 32位的中断服务是CPU的被动响应,通过硬件8259A进行中断控制,CPU接收到8259A的中断信号就会执行中断服务程序,可以在执行中断服务中再次被中断
- 执行
blk_dev_init()
进行块设备请求项结构的初始化
- linux的外设分类
- 块设备:将存储空间分解成若干相同大小的存储块,每个块有块号。常见的硬盘等
- 字符设备:以字符为单位进行I/O通信。常见的键盘、显示器等
- 进程与块设备通过内存中的缓冲区进行通信,请求项管理结构request[32]是操作系统管理缓冲区中缓冲块与块设备上的逻辑块间读写关系的数据结构
····
····待更新