Bootloader

我们知道计算机启动是从BIOS开始,再由BIOS决定从哪个设备启动以及启动顺序,比如先从DVD启动再从硬盘启动等。计算机启动后,BIOS根据配置找到启动设备,并读取这个设备的第0个扇区,把这个扇区的内容加载到0x7c00,之后让CPU从0x7c00开始执行,这时BIOS已经交出了计算机的控制权,由被加载的扇区程序接管计算机。
这第一个扇区的程序就叫Boot,它一般做一些准备工作,把操作系统内核加载进内存,并把控制权交给内核。由于Boot只能有一个扇区大小,即512字节,它所能做的工作很有限,因此它有可能不直接加载内核,而是加载一个叫Loader的程序,再由Loader加载内核。因为Loader不是BIOS直接加载的,所以它可以突破512字节的程序大小限制(在实模式下理论上可以达到1M)。如果Boot没有加载Loader而直接加载内核,我们可以把它叫做Bootloader。
Bootloader加载内核就要读取文件,在实模式下可以用BIOS的INT 13h中断。内核文件放在哪里,怎么查找读取,这里牵涉到文件系统,Bootloader要从硬盘(软盘)的文件系统中查找内核文件,因此Bootloader需要解析文件系统的能力。GRUB是一个专业的Bootloader,它对这些提供了很好的支持。
对于一个Toy操作系统来说,可以简单处理,把内核文件放到Bootloader之后,即从软盘的第1个扇区开始,这样我们可以不需要支持文件系统,直接读取扇区数据加载到内存即可。

实模式到保护模式

我们知道Intel x86系列CPU有实模式和保护模式,实模式从8086开始就有,保护模式从80386开始引入。为了兼容,Intel x86系列CPU都支持实模式。现代操作系统都是运行在保护模式下(Intel x86系列CPU)。计算机启动时,默认的工作模式是实模式,为了让内核能运行在保护模式下,Bootloader需要从实模式切换到保护模式,切换步骤如下:
  1. 准备好GDT(Global Descriptor Table)
  2. 关中断
  3. 加载GDT到GDTR寄存器
  4. 开启A20,让CPU寻址大于1M
  5. 开启CPU的保护模式,即把cr0寄存器第一个bit置1
  6. 跳转到保护模式代码
GDT是Intel CPU保护模式运行的核心数据结构,所有保护模式操作的数据都从GDT表开始查找,这里有GDT的详细介绍。
GDT中的每一个表项由8字节表示,如下图:


其中Access Byte和Flags如下图:


这里是详细说明。
GDTR是一个6字节的寄存器,有4字节表示GDT表的基地址,2字节表示GDT表的大小,即最大65536(实际值是65535,16位最大值是65535),每个表项8字节,那么GDT表最多可以有8192项。
实模式的寻址总线是20bits,为了让寻址超过1M,需要开启A20,可以通过以下指令开启:
in al, 0x92
    or al, 2
    out 0x92, al
把上述步骤完成之后,我们就进入保护模式了。在保护模式下我们要使用GDT通过GDT Selector完成,它是GDT表项相对于起始地址的偏移,因此它的值一般是0x0 0x8 0x10 0x18等。

ELF文件

Bootloader程序是原始可执行文件,如果程序由汇编写成,汇编编译器编译生成的文件就是原始可执行文件,也可以使用C语言编写,编译成可执行文件之后通过objcopy转换成原始可执行文件,这篇文章介绍了用C语言写Bootloader。
那么内核文件是什么格式的呢?跟Bootloader一样的当然可以。内核一般使用C语言编写,每次编译链接完成之后调用objcopy是可以的。我们也可以支持通用的可执行文件格式,ELF(Executable and Linkable Format)即是一种通用的格式,它的维基百科。
ELF文件有两种视图(View),链接视图和执行视图,如下图:

链接视图通过Section Header Table描述,执行视图通过Program Header Table描述。Section Header Table描述了所有Section的信息,包括所在的文件偏移和大小等;Program Header Table描述了所有Segment的信息,即Text Segment, Data Segment和BSS Segment,每个Segment中包含了一个或多个Section。

对于加载可执行文件,我们只需关注执行视图,即解析ELF文件,遍历Program Header Table中的每一项,把每个Program Header描述的Segment加载到对应的虚拟地址即可,然后从ELF header中取出Entry的地址,跳转过去就开始执行了。对于ELF格式的内核文件来说,这个工作就需要由Bootloader完成。Bootloader支持ELF内核文件加载之后,用C语言编写的内核编译完成之后就不需要objcopy了。

为什么写操作系统

首先是兴趣,在现在这个时代,写操作系统几乎没有实用价值,只能是一个Toy,在写一个Toy OS时,可以学习掌握很多知识,并把这些知识贯穿实用起来。操作系统是一个复杂的系统,牵涉到的东西很多,我相信写操作系统可以帮助理解现代操作系统及其它底层知识。我目前才刚开始写,代码放在Github上。

操作系统实现(一):从Bootloader到ELF内核相关推荐

  1. BootLoader与Linux内核的参数传递

    在嵌入式系统中,BootLoader 是用来初始化硬件,加载内核,传递参数.因为嵌入式系统的硬件环境各不相同,所以嵌入式系统的BootLoader 也各不相同,其中比较通用的是U-Boot,它支持不同 ...

  2. 深度linux内核升级,深度操作系统 2020.11.11 更新发布:内核升级

    原标题:深度操作系统 2020.11.11 更新发布:内核升级 IT之家11月11日消息 今日,深度操作系统宣布2020.11.11 更新现已发布.本次更新包括升级内核.Debian 10.6 仓库以 ...

  3. 「操作系统」什么是用户态和内核态?为什么要区分

    「操作系统」什么是用户态和内核态?为什么要区分 参考&鸣谢 从根上理解用户态与内核态 程序员阿星 并发编程(二十六)内核态和用户态 Lovely小猫 操作系统之内核态与用户态 fimm 文章目 ...

  4. 跟我一起写操作系统(二)——史上最简单的内核

    转载注明出处: http://www.cnblogs.com/lucasysfeng/p/4847662.html 上一讲地址:http://www.cnblogs.com/lucasysfeng/p ...

  5. BootLoader简介——linux内核的引导

    1.BootLoader 在CPU上电启动时,一般连内存控制器都没有初始化过,根本无法在主存中运行程序,更不可能处在Linux内核启动环境中,为了初始化CPU及其他外设,使得Linux内核可以在系统主 ...

  6. 操作系统真相还原——第6章 完善内核

    函数底层调用约定 cdecl:函数参数由栈进行传递,从右向左顺序入栈,栈空间由调用者清理,函数的返回值存储在EAX 寄存器. syscall:参数从右到左入校.参数列袤的大小被放置在AL 寄存器中 o ...

  7. 西电软工操作系统实验:编译Ubuntu18.04新内核并添加系统调用(含代码以及详细分析)

    西电软工操作系统实验一:编译Linux内核 目录 (一)前言 (二)实验内容 (三)实验环境 (四)实验过程 4.1安装虚拟机 4.2虚拟机换源 4.3 添加系统调用内核 4.4 下载编译所需的软件依 ...

  8. 趣谈Linuxn操作系统:03你可以把Linux内核当成一家软件外包公司的老板

    [该文章知识来源<极客时间刘超老师-趣谈Linux操作系统>向大神致敬. 趣谈Linux操作系统(极客时间)链接: http://gk.link/a/10iXZ 欢迎大家来一起交流学习,一 ...

  9. 如何知道当前操作系统是centos的哪个版本和内核版本?

    1.用redhat-lsb查看 yum -y install redhat-lsb 安装完成后使用:lsb_release -a 2.使用uname -a查看内核版本 3.使用cat /proc/ve ...

最新文章

  1. 学习这么多算法到底在解决哪些问题?深度学习之外,我们要选择谁?
  2. 从 RxJS 到 Flink:如何处理数据流?
  3. binder,hwbinder,vndbinder之间的关系
  4. red6.4 mysql_rhel6.4 yum本地yum源配置
  5. 价值6000的信息分类系统源码
  6. 计算机大赛软件应用与开发,我院学生在2019年中国大学生计算机设计大赛“软件应用与开发”决赛中荣获佳绩...
  7. fastdfs暗转 linux_Linux下安装fastDFS
  8. 用TensorFlow训练一个目标检测器(手把手教学版)
  9. paip. VS2010版本控制tfs使用总结
  10. 手机邮箱怎么弄_手机邮箱设置
  11. Revealing ecosystem services relationships and their driving factors for five basins of Beijing(1)
  12. 一些API函数(Console居多)Console API
  13. t600和t1200的区别
  14. Excel数据透视表经典教程一《数据透视规范》
  15. 电脑死机,虚拟机里面的系统开不了
  16. 努努小说通用爬取——多线程
  17. 海外媒体发稿技巧:滚金石海内外媒体教您海外发稿技巧
  18. Java“白皮书”的关键术语
  19. 游戏服务器系统的选择界面,cocos2dx网游选服界面制作三:服务器单个item界面包装...
  20. 立创商城的元器件库导入AD16(导入自己的库)

热门文章

  1. UVA11419 我是SAM
  2. 【Linux 内核】CFS 调度器 ① ( CFS 完全公平调度器概念 | CFS 调度器虚拟时钟 Virtual Runtime 概念 | 四种进程优先级 | 五种调度类 )
  3. 【Groovy】自定义 Xml 生成器 BuilderSupport ( nodeCompleted 方法获取节点闭合信息 )
  4. 【Groovy】闭包 Closure ( 闭包类 Closure 简介 | 闭包 parameterTypes 和 maximumNumberOfParameters 成员用法 )
  5. 【设计模式】访问者模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
  6. 【Android 安全】DEX 加密 ( 代理 Application 开发 | multiple-dex-core 依赖库开发 | 配置元数据 | 获取 apk 文件并准备相关目录 )
  7. 【设计模式】原型模式 ( 概念简介 | 使用场景 | 优缺点 | 基本用法 )
  8. [Spring cloud 一步步实现广告系统] 18. 查询返回广告创意
  9. memcached 扩展安装(windows)
  10. 单例模式(winform唯一页面)