ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] ## 写在前面 下面有不熟悉的代码很正常,这节课主要目的是让我们跑一下。 引用编辑恢复评论里的一句话: 初入门一个上下左右都有很多牵扯的复杂网状系统的时候,一定要像张无忌学太极剑一样,只记住剑意,选择性忘记忽略一些细枝末节。不然的话,很可能就会被带跑偏,沉溺于一些奇奇怪怪暂时又不太重要的东西。 **学习可以先抓主线,慢慢消化甚至优化细节** ## PC 机的引导流程 这里课程没有从 PC 的引导程序开始写起,原因是目前我们的知识储备还不够,所以先借用一下 GRUB 引导程序,只要我们的 PC 机上安装了 Ubuntu Linux 操作系统,GRUB 就已经存在了。 下面附上我们要跑起来的Hello OS的引导流程图: ![](https://img.kancloud.cn/11/9f/119f69bcf46dcfe7a946f012a8649907_4335x3170.png) ## Hello OS 引导汇编代码 ```asm ;彭东 @ 2021.01.09 MBT_HDR_FLAGS EQU 0x00010003 MBT_HDR_MAGIC EQU 0x1BADB002 ;多引导协议头魔数 MBT_HDR2_MAGIC EQU 0xe85250d6 ;第二版多引导协议头魔数 global _start ;导出_start符号 extern main ;导入外部的main函数符号 [section .start.text] ;定义.start.text代码节 [bits 32] ;汇编成32位代码 _start: jmp _entry ALIGN 8 mbt_hdr: dd MBT_HDR_MAGIC dd MBT_HDR_FLAGS dd -(MBT_HDR_MAGIC+MBT_HDR_FLAGS) dd mbt_hdr dd _start dd 0 dd 0 dd _entry ;以上是GRUB所需要的头 ALIGN 8 mbt2_hdr: DD MBT_HDR2_MAGIC DD 0 DD mbt2_hdr_end - mbt2_hdr DD -(MBT_HDR2_MAGIC + 0 + (mbt2_hdr_end - mbt2_hdr)) DW 2, 0 DD 24 DD mbt2_hdr DD _start DD 0 DD 0 DW 3, 0 DD 12 DD _entry DD 0 DW 0, 0 DD 8 mbt2_hdr_end: ;以上是GRUB2所需要的头 ;包含两个头是为了同时兼容GRUB、GRUB2 ALIGN 8 _entry: ;关中断 cli ;关不可屏蔽中断 in al, 0x70 or al, 0x80 out 0x70,al ;重新加载GDT lgdt [GDT_PTR] jmp dword 0x8 :_32bits_mode _32bits_mode: ;下面初始化C语言可能会用到的寄存器 mov ax, 0x10 mov ds, ax mov ss, ax mov es, ax mov fs, ax mov gs, ax xor eax,eax xor ebx,ebx xor ecx,ecx xor edx,edx xor edi,edi xor esi,esi xor ebp,ebp xor esp,esp ;初始化栈,C语言需要栈才能工作 mov esp,0x9000 ;调用C语言函数main call main ;让CPU停止执行指令 halt_step: halt jmp halt_step GDT_START: knull_dsc: dq 0 kcode_dsc: dq 0x00cf9e000000ffff kdata_dsc: dq 0x00cf92000000ffff k16cd_dsc: dq 0x00009e000000ffff k16da_dsc: dq 0x000092000000ffff GDT_END: GDT_PTR: GDTLEN dw GDT_END-GDT_START-1 GDTBASE dd GDT_START ``` 1. 代码 1~40 行,用汇编定义的 GRUB 的多引导协议头,其实就是一定格式的数据,我们的 Hello OS 是用 GRUB 引导的,当然要遵循 GRUB 的多引导协议标准,让 GRUB 能识别我们的 Hello OS。之所以有两个引导头,是为了兼容 GRUB1 和 GRUB2。 2. 代码 44~52 行,关掉中断,设定 CPU 的工作模式。你现在可能不懂,没事儿,后面 CPU 相关的课程我们会专门再研究它。 3. 代码 54~73 行,初始化 CPU 的寄存器和 C 语言的运行环境。 4. 代码 78~87 行,GDT_START 开始的,是 CPU 工作模式所需要的数据,同样,后面讲 CPU 时会专门介绍。 ## Hello OS 的主函数 ```c //彭东 @ 2021.01.09 #include "vgastr.h" void main() { printf("Hello OS!"); return; } ``` ## 控制计算机屏幕 附:显卡的字符模式的工作细节 它把屏幕分成 24 行,每行 80 个字符,把这(24*80)个位置映射到以 0xb8000 地址开始的内存中,每两个字节对应一个字符,其中一个字节是字符的 ASCII 码,另一个字节为字符的颜色值。如下图所示: ![](https://img.kancloud.cn/83/59/8359572ac3177435b302891eb52e5267_3530x1605.png) ## 编译和安装 Hello OS 下载代码 `git clone https://gitee.com/lmos/cosmos.git` 目录结构如下 ![](https://img.kancloud.cn/1b/23/1b238ca1630e1a1239ef1e2f69194963_761x157.png) * entry.asm:是一段汇编代码,用作GRUB引导调用,关掉中断,设定CPU工作模式,初始化寄存器及C语言运行环境等; * hello.lds:进行链接调用,代码简单看看,反正也看不懂: ```lds ENTRY(_start) OUTPUT_ARCH(i386) OUTPUT_FORMAT(elf32-i386) SECTIONS { . = 0x200000; __begin_start_text = .; .start.text : ALIGN(4) { *(.start.text) } __end_start_text = .; ​ __begin_text = .; .text : ALIGN(4) { *(.text) } __end_text = .; ​ __begin_data = .; .data : ALIGN(4) { *(.data) } __end_data = .; ​ __begin_rodata = .; .rodata : ALIGN(4) { *(.rodata) *(.rodata.*) } __end_rodata = .; ​ __begin_kstrtab = .; .kstrtab : ALIGN(4) { *(.kstrtab) } __end_kstrtab = .; ​ __begin_bss = .; .bss : ALIGN(4) { *(.bss) } __end_bss = .; } ``` * install.md:需要将这个文件里的内容复制到GRUB的cfg配置文件中,才能使电脑开机时可以找到我们的Hello OS; * main.c:我们Hello OS的主函数,它调用的printf可不是常见的C语言库函数哦,而是我们自己实现的printf! ![](https://img.kancloud.cn/28/aa/28aa015628cedcb775ca8b1c4f4075e8_316x138.png) 即下面要讲的vgastr.h; * vgastr.h:控制计算机屏幕VGABIOS固件程序显示特定字符,后面详细介绍; * Makefile:利用make工具来实现编译源代码,主要是将entry.asm、main.c、vgastr.h编译并链接。 ### 编译操作系统 ### 流程 ![](https://img.kancloud.cn/99/58/9958c1d2c15c80931515df2e99bc2faa_4378x4923.png) ### 编译 ![](https://img.kancloud.cn/51/e4/51e42c4957f0a29fac8897c7d0150319_951x346.gif) 提示 **nasm**编译器没有安装 安装一下:`sudo apt-get install nasm` 继续编译: ![](https://img.kancloud.cn/a5/44/a54444a4b24020d68f280919c3fefb4b_951x416.gif) ### 安装 #### 找到boot目录挂载分区: `df /boot/` ![](https://img.kancloud.cn/9e/f8/9ef80bac3e6e5516cf6e2fa5b4dd0aac_699x78.png) 可以看到虚拟机中ubuntu16.04的系统GRUB引导是在硬盘的第**五**分区 然后**打开文件夹中的install.md,复制粘贴到\***\*/boot/grub/grub.cfg中\*\*,install.md主要是加载我们的Hello OS的启动项: ![](https://img.kancloud.cn/5b/c6/5bc6897d36730fd29560dc9fa8d1315c_815x331.png) ```vim /boot/grub/grub.cfg``` ![](https://img.kancloud.cn/54/92/5492da23038944a861c3a3a4dafa780f_968x712.png) 将 **Hello OS.bin** 文件复制到 /boot/ 目录下 ![![](https://img.kancloud.cn/f7/9e/f79e41babb6e63968e5ce71c78c917bb_874x351.png)](images/screenshot_1628234267320.png) ### 设置GRUB引导界面时间 **执行命令**:`sudo gedit /etc/default/grub` 修改图示内容 ![](https://img.kancloud.cn/2c/88/2c88bb5404a824b82c4229e1721039bc_868x289.png) **更新grub配置** `sudo update-grub` ![](https://img.kancloud.cn/ff/4d/ff4d961440acd2de714a07c470f50c81_610x213.png) #### 重启查看效果 **以上一切看起来都没问题,也没有报错,但是,遇到坑了!!** 我惊奇的发现,我重启系统的时候,启动项里竟然没有HelloOs选项,经过反复排错(用户权限问题,代码段放置位置问题,等等)后,折腾一个小时后,我终于找到问题了:**设置GRUB引导界面时间,更新grub配置,一定要放在配置/boot/grub/grub.cfg后进行!** 查看一下效果: ![](https://img.kancloud.cn/fe/5a/fe5a6e8f6ffd70fa6be5108aca743def_1051x715.gif) >0x7C00 由来的一种说法。当时,搭配的操作系统是86-DOS。这个操作系统需要的内存最少是32KB。我们知道,内存地址从0x0000开始编号,32KB的内存就是0x0000~0x7FFF。8088芯片本身需要占用0x0000~0x03FF,用来保存各种中断处理程序的储存位置。(主引导记录本身就是中断信号INT 19h的处理程序。)所以,内存只剩下0x0400~0x7FFF可以使用。为了把尽量多的连续内存留给操作系统,主引导记录就被放到了内存地址的尾部。由于一个扇区是512字节,主引导记录本身也会产生数据,需要另外留出512字节保存(自己 和 产生)。所以,它的预留位置就变成了:     0x7FFF - 512 - 512 + 1 = 0x7C00