6.828设计总结---内核的加载

什么是微内核

微内核主要用于解决宏内核操作系统在扩展性、可靠性等方面遇到的问题。区别于宏内核的左右系统功能都实现在内核中,微内核只在内核中保留必要的系统功能,比如基础的IPC、进程调度机制等,而其他诸如文件系统、网络IO等功能都在用户空间实现。
现有的微内核:QNX、SEL家族(更新到seL4)

什么是实模式与保护模式?

实模式指的就是直接使用物理地址对内存进行访问的方式。早期CPU一般只有16位的运行环境,将整个物理内存进行分段,代码和数据保存在各个段中。普通程序在运行时,可能会修改与系统相关的内存段中的值,这是十分危险的。后来,CPU发展到32位之后,寻址空间达到了4GB,为了对内存进行保护以及访问更多的地址空间,引入了保护模式。
保护模式提供段页式的内存管理机制,以及多任务的执行。保护模式的段基址是一个指向全局描述符表的指针,这个全局描述符保存着每一段相关的信息。
但是本着向下兼容的思想,大多数CPU都保留了实模式,并且在CPU上电或复位时启用,加载操作系统的时候必须将工作模式切换为保护模式。

jos如何实现实模式到保护模式的切换?

jos模式的切换是发生在操作系统引导与加载的时候,所以下面直接介绍一下jos引导与加载的过程。
主要分为两个阶段:

  1. 引导程序的加载
  2. 内核的加载

引导程序的加载

PC上电启动,运行为实模式,硬件将直接存储在”磁盘“的BIOS加载到内存中。进入实模式运行后,下一条指令CS:IP就是BIOS所在的物理地址。(BIOS主要是完成加电自检和硬件初始化的工作,之后将处理器控制权转交)将处理器的设备初始化完成之后,从存储器中加载内核引导器(boot loader)。boot loader的主要作用是切换处理器的工作模式,将内核从磁盘加载到内存。
bootloader的第一条指令就是cli,关中断(内核准备好之后再打开)。在实模式下,处理器有8个16位通用寄存器,但是处理器需要发送20位的地址给存储器(实模式的地址空间是1MB),所以需要使用cs,ss,es,ds提供额外位。

jos中模式的切换在boot.s中实现:
要从实模式切换到保护模式,首先需要开启更多的地址线。
这就首先涉及到A20 Gate的开启,通过键盘控制器的输出端口第二位的电平来判定是否启用第21位地址线,当其电平为高时,就启用第21位的地址线,让处理器能够寻址32位。主要分为两部分:

  1. 一部分是向键盘输入缓冲器(0x64端口)写一个控制命令(0xd1), 0xd1表示向P2输出端口写数据,其具体数据就是0x60端口输入
  2. 具体写向P2输出端口的数据,向键盘输入缓冲器(0x60端口)写入0xdf, 其二进制表示0b11011111, 其中第2位则为A20选通位,这时A20 Gate开启

至此,可以使用32位地址线了,但是实模式到保护模式的实际切换是由CR0寄存器的最低位(保护模式使能位)值1决定的。

  1. 在GDTR(段描述符表寄存器)中存入GDT(段描述符表)的首地址
  2. 将CR0寄存器的PE位置1,开启处理器的保护模式

使用ljmp指令跳转进入保护模式,在保护模式中需要重新对一些32位的段寄存器进行初始化,对堆栈指针ESP进行初始化,然后进入bootmain函数完成对内核的加载。

内核的加载(ELF文件的加载)

内核的加载就是从磁盘中读取ELF可执行文件的过程,所以需要首先了解ELF文件格式。

什么是ELF文件?

ELF文件是一种文件格式,一般可以是可执行文件、目标文件、共享库和二进制文件。Linux中主要的可执行文件格式就是ELF文件。操作系统中通过加载可执行文件来实现对程序和数据的访问。

ELF文件结构
在这里插入图片描述
jos中ELF文件头的数据结构和程序头表的数据结构:

struct Elf {
    uint32_t e_magic;   // must equal ELF_MAGIC
    uint8_t e_elf[12];
    uint16_t e_type;
    uint16_t e_machine;
    uint32_t e_version;
    uint32_t e_entry;
    uint32_t e_phoff;	// 程序节头表数据的信息,所有的段
    uint32_t e_shoff;
    uint32_t e_flags;
    uint16_t e_ehsize;
    uint16_t e_phentsize;
    uint16_t e_phnum;	// 段的数量
    uint16_t e_shentsize;
    uint16_t e_shnum;
    uint16_t e_shstrndx;
};
struct Proghdr {
    uint32_t p_type;
    uint32_t p_offset;
    uint32_t p_va;
    uint32_t p_pa;
    uint32_t p_filesz;
    uint32_t p_memsz;
    uint32_t p_flags;
    uint32_t p_align;
};

过程:

  1. 从磁盘的1号扇区中读取读取8个扇区大小的ELF格式kernel到物理地址0x10000,检查其是否是有效的ELF文件;
  2. 需要去读取程序头表中指明 的所有段,第一个segment对象的地址存放在ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff)中,则 结束的地址为:eph = ph + ELFHDR->e_phnum;然后就可以遍历这个proghdr数组了。至此,内核完全加载完毕。
 for (; ph < eph; ph++)
    readseg(ph->p_pa, ph->p_memsz, ph->p_offset);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值