[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
- 微服务
- 服务器相关
- 操作系统
- 极客时间操作系统实战笔记
- 01 程序的运行过程:从代码到机器运行
- 02 几行汇编几行C:实现一个最简单的内核
- 03 黑盒之中有什么:内核结构与设计
- Rust
- 入门:Rust开发一个简单的web服务器
- Rust的引用和租借
- 函数与函数指针
- Rust中如何面向对象编程
- 构建单线程web服务器
- 在服务器中增加线程池提高吞吐
- Java
- 并发编程
- 并发基础
- 1.创建并启动线程
- 2.java线程生命周期以及start源码剖析
- 3.采用多线程模拟银行排队叫号
- 4.Runnable接口存在的必要性
- 5.策略模式在Thread和Runnable中的应用分析
- 6.Daemon线程的创建以及使用场景分析
- 7.线程ID,优先级
- 8.Thread的join方法
- 9.Thread中断Interrupt方法学习&采用优雅的方式结束线程生命周期
- 10.编写ThreadService实现暴力结束线程
- 11.线程同步问题以及synchronized的引入
- 12.同步代码块以及同步方法之间的区别和关系
- 13.通过实验分析This锁和Class锁的存在
- 14.多线程死锁分析以及案例介绍
- 15.线程间通信快速入门,使用wait和notify进行线程间的数据通信
- 16.多Product多Consumer之间的通讯导致出现程序假死的原因分析
- 17.使用notifyAll完善多线程下的生产者消费者模型
- 18.wait和sleep的本质区别
- 19.完善数据采集程序
- 20.如何实现一个自己的显式锁Lock
- 21.addShutdownHook给你的程序注入钩子
- 22.如何捕获线程运行期间的异常
- 23.ThreadGroup API介绍
- 24.线程池原理与自定义线程池一
- 25.给线程池增加拒绝策略以及停止方法
- 26.给线程池增加自动扩充,闲时自动回收线程的功能
- JVM
- C&C++
- GDB调试工具笔记
- C&C++基础
- 一个例子理解C语言数据类型的本质
- 字节顺序-大小端模式
- Php
- Php源码阅读笔记
- Swoole相关
- Swoole基础
- php的五种运行模式
- FPM模式的生命周期
- OSI网络七层图片速查
- IP/TCP/UPD/HTTP
- swoole源代码编译安装
- 安全相关
- MySql
- Mysql基础
- 1.事务与锁
- 2.事务隔离级别与IO的关系
- 3.mysql锁机制与结构
- 4.mysql结构与sql执行
- 5.mysql物理文件
- 6.mysql性能问题
- Docker&K8s
- Docker安装java8
- Redis
- 分布式部署相关
- Redis的主从复制
- Redis的哨兵
- redis-Cluster分区方案&应用场景
- redis-Cluster哈希虚拟槽&简单搭建
- redis-Cluster redis-trib.rb 搭建&原理
- redis-Cluster集群的伸缩调优
- 源码阅读笔记
- Mq
- ELK
- ElasticSearch
- Logstash
- Kibana
- 一些好玩的东西
- 一次折腾了几天的大华摄像头调试经历
- 搬砖实用代码
- python读取excel拼接sql
- mysql大批量插入数据四种方法
- composer好用的镜像源
- ab
- 环境搭建与配置
- face_recognition本地调试笔记
- 虚拟机配置静态ip
- Centos7 Init Shell
- 发布自己的Composer包
- git推送一直失败怎么办
- Beyond Compare过期解决办法
- 我的Navicat for Mysql
- 小错误解决办法
- CLoin报错CreateProcess error=216
- mysql error You must reset your password using ALTER USER statement before executing this statement.
- VM无法连接到虚拟机
- Jetbrains相关
- IntelliJ IDEA 笔记
- CLoin的配置与使用
- PhpStormDocker环境下配置Xdebug
- PhpStorm advanced metadata
- PhpStorm PHP_CodeSniffer