linux内核
(以下内容是我目前的理解,可能有不对的地方)
计算机加电开机后,BIOS会把启动盘的0磁道0扇区的前512字节加载到0x7c00处(这个加载过程是bios负责的,操作系统不用管),然后从这开始运行,这512字节一般称为引导程序(bootsect.S),它负责把操作系统程序加载到内存0x10000处(这个加载是用bios中断实现的,中断向量表在0x0地址处,所以不能直接拷贝到0x0处),然后拷贝到0x0处,然后进入保护模式,然后跳转到0x0处开始运行。
在进入保护模式之前是实模式,两者寻址方式不同:
实模式地址直接给出:段地址<<4 + 偏移地址,反正就是给两个地址,一个地址左移4位加上另一个地址就是实际的地址了。
保护模式寻址就涉及GDT,GDT是一个数组,每项占8个字节,指定了一段内存,包括内存基地址,这段内存是干什么的,段限长,特权级等等。现在要访问一个地址,就不能直接给两个地址组合一下了,要指定GDT中的一项,这就给出了段基地址,然后指定段偏移,这两者相加,就是实际地址了。
如果开启了分页查询,上面得到的32位地址,还要根据页目录和页表再变换一下,才能得到实际物理地址。
操作系统除了开始bootsect.S是在实模式下运行,剩下的都是运行在保护模式,保护模式所有的段都要在GDT中定义,不能直接访问一个内存地址,所以可以根据GDT了解操作系统。
赵炯老师的《Linux内核完全剖析》第4章最后实现了一个简单的多任务内核实例,两个任务交替在时钟中断下交替运行,一个任务打印A,一个任务打印B。
在这里说下任务(进程)在内核中是怎么表示的,任务是有数据段、代码段(数据段、代码段由LDT指定,LDT和GDT差不多,不过GDT是整个内核公用的,LDT是一个任务一个,指定了这个任务的代码段,数据段)、堆栈段(又分为内核栈、用户栈),任务运行时是要用到各个寄存器的。
表示任务的数据结构是:任务状态段TSS,这是一个段,放在内存中,里面存放了通用寄存器、标志寄存器的值,还有LDT选择符,不同特权级的堆栈(SS、ESP),还有前一个任务的链接(TSS选择符)。
这样一个数据结构就可以完整的表示一个任务了。
所以上面简单的多任务内核,首先有内核代码段、内核数据段,两个任务,每个任务一个TSS,一个LDT。
参考:
赵炯《Linux内核完全剖析——基于0.12内核》
于渊《ORANGE’S:一个操作系统的实现》
Randal E.Bryant David R.O’Hallaron《深入理解计算机系统》
王爽《汇编语言》
void的视频
郑州大学 汇编语言程序设计
国防科技大学 计算机原理
哈尔滨工业大学 *军老师 操作系统