我是如何学习写一个操作系统(二):操作系统的启动之Bootloader
前言
今天本来的任务看书和把之前写的FragileOS整理一下,但是到现在还在摸鱼,书也只看一点。后来整理了一下写这个系列的思路,原本的目的是对操作系统原理性的学习和对之前写的一个玩具型操作系统的回顾,就是想对操作系统的知识的轮廓能有一个了解,现在想来想减少对之前写的系统的回顾,毕竟也只有2000多行,但是还是要有对整个思路的展现。然后增加对Linux 0.12源码的一些学习。所以离标题可能比较远了一点,但是就这样吧
什么是操作系统

原本这一节是写计算机系统和操作系统概述的,但是写到一半觉得太水就删了。就总结几句,后面用到什么就补什么。计算机系统的概述应该属于计算机组成原理的内容,这俩部分也是《操作系统:精髓和设计原理》的第一二章。但是觉得如果对于想学习操作系统内部的代码的话,换成汇编的内容会更好。
进入正题,操作系统是什么
对于计算机来说最根本的运行方式,就是取指执行
对于在屏幕上输出Hello,World!的过程,首先CPU拿到内存中的指令,这些指令是通知CPU把存在某个内存中的'H''E''L'等移动到显存位置,这样在屏幕上就可以看到这些字符了。这就是计算机最原始的运行方式
而操作系统就是对硬件层面的抽象,让我们不用在直面硬件,如果想要再次在屏幕输出字符,只要直接调用操作系统的write(windows下的好像是这个名),C语言中的printf下就是一个系统调用
当然操作系统绝对是比想象中的庞大的多,操作系统还对内存、终端、磁盘、网络和文件等等进行管理,光windows 2000应该就有3000多万行的代码了。当然有简陋的内存、进程管理和文件系统的玩具型内核,只要几千行代码就可以完成了。
操作系统的启动
对于X86架构的计算机,开机时一共做这几件事
开机时的CS = 0xFFFF, IP = 0x0000
这时候的CPU处理实模式,也就是寻址的方式是CS:IP (实模式和保护模式属于CPU的工作模式,其中比较大的区别就是寻址的方式)
寻址0xFFFF0
检查硬件设备,像键盘显示器之类的
将磁盘0磁道0扇区读入0x7c00处
会从这里读入512字节,也就是传说中的引导程序,这里放着计算机执行的第一段代码
设置cs = 0x7c00 ip = 0x0000
FragileOS/boot
这个是我之前写的FragileOS的boot,采用的是Intel汇编格式
主要的逻辑就是:
- 先加载到0x7c00位置
- 进行初始化操作
- 调用CPU提供的中断来读取数据
- 读取完毕后直接跳到内核的起始位置,也就是引导结束了
(部分代码)
org 0x7c00; ;加载到内存0x7c00处
LoadAddr EQU 08000h ;内核的内存地址
BufferAddr EQU 7E0h ;读取扇区的时候进行的缓存
BaseOfStack EQU 07c00h
entry:
mov ax, 0 ;进行寄存器的初始化操作
mov ss, ax
mov ds, ax
mov ax, BufferAddr
mov es, ax ;ES:BX 数据存储缓冲区,指示扇区加载后放置的地址
mov ax, 0
mov ss, ax
mov sp, BaseOfStack
mov di, ax
mov si, ax
mov BX, 0 ;ES:BX 数据存储缓冲区
mov CH, 1 ;CH 用来存储柱面号
mov DH, 0 ;DH 用来存储磁头号
mov CL, 0 ;CL 用来存储扇区号
read_floppy: ;每次都把扇区写入缓存地址07E00处
cmp byte [load_count], 0 ;比较load_count地址处的值,如果=0就跳转到begin_load
je begin_load
mov bx, 0
inc CL
mov AH, 0x02 ;AH = 02 表示要做的是读盘操作
mov AL, 1 ;AL 表示要练习读取几个扇区
mov DL, 0 ;驱动器编号,一般我们只有一个软盘驱动器,所以写死
int 0x13 ;调用BIOS中断实现磁盘读取功能
jc fin
Linux 0.12/boot
Linux 0.12的boot自然比上面的复杂的多,Linux采用的boot的汇编是as86格式的,其余的汇编采用的都是AT&T
Linux 0.12下的boot一共有三个文件:
- bootsect
- head
- setup
(代码太长不全部贴了,有需要的可以私信我)
bootsect
bootsect的主要作用就是把自己移动到0x90000处执行,然后再加载setup模块 (也就是setup.s)到bootsect的后面,再把system模块加载到0x10000处,这个也就是内核的主要部分
bootsect的开头是一些常量的定义
SETUPLEN = 4 ! nr of setup-sectors
BOOTSEG = 0x07c0 ! original address of boot-sector
INITSEG = 0x9000 ! we move boot here - out of the way
SETUPSEG = 0x9020 ! setup starts here
SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
ENDSEG = SYSSEG + SYSSIZE ! where to stop loading
_start先设置好目的地址和源地址
ds:si和es:di
然后执行rep指令
rep指令是重复的意思,它以cx寄存器的值为判断,如果cx的值为0就停止
movw指令
开始从[si]处移动cx个字到[di]处,这里也就是一共移动了256个字,512字节
最后跳转到0x9000开始执行
_start:
mov ax,#BOOTSEG
mov ds,ax
mov ax,#INITSEG
mov es,ax
mov cx,#256
sub si,si
sub di,di
rep
movw
jmpi go,INITSEG
- 现在的这些代码都是在0x90000后的
- 先重新设置段寄存器和栈指针
go: mov ax,cs
mov ds,ax
mov es,ax
! put stack at 0x9ff00.
mov ss,ax
mov sp,#0xFF00 ! arbitrary value >>512
这一部分和我之前的一样,就是调用中断来读取磁盘内容,只是Linux读取的是在第二扇区的setup模块
如果失败就重新设置驱动器然后跳回重新读取
成功就跳到ok_load_setup
ok_load_setup是设置根文件系统设备的,并且读入SYSTEM模块 (内核的主要部分)到0x10000处,结尾是跳到SETUP模块
load_setup:
mov dx,#0x0000 ! drive 0, head 0
mov cx,#0x0002 ! sector 2, track 0
mov bx,#0x0200 ! address = 512, in INITSEG
mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors
int 0x13 ! read it
jnc ok_load_setup ! ok - continue
mov dx,#0x0000
mov ax,#0x0000 ! reset the diskette
int 0x13
j load_setup
小结
一个简单的boot引导程序,顾名思义就是把做一些引导工作的,进行一些初始化设置再读入真正的内核部分,进入OS。
其实Linux 0.12一个完整的boot应该还包括setup.s用来完成OS启动前最后的设置 (进入保护模式等),head.s则是进入之后的设置。但是因为这两部分包含了一些其它重要概念,所以打算再下一篇写。
我是如何学习写一个操作系统(二):操作系统的启动之Bootloader的更多相关文章
- 自制 os 极简教程1:写一个操作系统有多难
为什么叫极简教程呢?听我慢慢说 不知道正在阅读本文的你,是否是因为想自己动手写一个操作系统.我觉得可能每个程序员都有个操作系统梦,或许是想亲自动手写出来一个,或许是想彻底吃透操作系统的知识.不论是为了 ...
- spring boot开发,jar包一个一个来启动太麻烦了,写一个bat文件一键启动
spring boot开发,jar包一个一个来启动太麻烦了,写一个bat文件一键启动 @echo offcd D:\workProject\bushustart cmd /c "title ...
- Linux内核学习--写一个c程序,并在内核中编译,运行
20140506 今天开始学习伟大的开源代表作:Linux内核.之前的工作流于几个简单命令的应用,因着对Android操作系统的情愫,“忍不住”跟随陈利君老师的步伐,开启OS内核之旅.学习路径之一是直 ...
- Android学习--写一个发送短信的apk,注意布局文件的处理过程!!!
刚开始写Android程序如图发现使用了findViewById方法之后输出的话居然是null(空指针错误),也就是说这个方法没有成功.网上说这样写是在activity_main .xml去找这个ID ...
- Qt 利用XML文档,写一个程序集合 二
接上一篇文章https://www.cnblogs.com/DreamDog/p/9213915.html XML文档的读写 一个根节点,下面每一个子节点代表一个子程序,内容为子程序名字,图标路径,e ...
- 一个人写的操作系统 - Sparrow OS
一个人写的操作系统 - Sparrow OS 自己写一个操作系统,这是在过去的几年里我一直为之努力的目标,现在终于完成了. 缘起 自己动手写操作系统的动机最初来自于学习Linux遇到的困难. 我是一个 ...
- 一起学习造轮子(二):从零开始写一个Redux
本文是一起学习造轮子系列的第二篇,本篇我们将从零开始写一个小巧完整的Redux,本系列文章将会选取一些前端比较经典的轮子进行源码分析,并且从零开始逐步实现,本系列将会学习Promises/A+,Red ...
- React学习及实例开发(二)——用Ant Design写一个简单页面
本文基于React v16.4.1 初学react,有理解不对的地方,欢迎批评指正^_^ 一.引入Ant Design 1.安装antd yarn add antd 2.引入 react-app-re ...
- WCF入门教程(四)通过Host代码方式来承载服务 一个WCF使用TCP协议进行通协的例子 jquery ajax调用WCF,采用System.ServiceModel.WebHttpBinding System.ServiceModel.WSHttpBinding协议 学习WCF笔记之二 无废话WCF入门教程一[什么是WCF]
WCF入门教程(四)通过Host代码方式来承载服务 Posted on 2014-05-15 13:03 停留的风 阅读(7681) 评论(0) 编辑 收藏 WCF入门教程(四)通过Host代码方式来 ...
随机推荐
- 和朱晔一起复习Java并发(四):Atomic
本节我们来研究下并发包中的Atomic类型. AtomicXXX和XXXAdder以及XXXAccumulator性能测试 先来一把性能测试,对比一下AtomicLong(1.5出来的).LongAd ...
- 个人永久性免费-Excel催化剂功能第83波-遍历文件夹内文件信息特别是图像、音视频等特有信息
在过往的功能中,有体现出在Excel上管理文件的极大优势,在文件的信息元数据中,有图片和音视频这两类特有的属性数据,此篇对过往功能的一个补充,特别增加了图片和音视频信息的遍历功能. 使用场景 在文件管 ...
- 个人永久性免费-Excel催化剂功能第38波-比Vlookup更好用的查找引用函数
谈起Excel的函数,有一个函数生来自带明星光环,在表哥表姐群体中无人不知,介绍它的教程更是铺天盖地,此乃VLOOKUP函数也.今天Excel催化剂在这里冒着被火喷的风险,大胆地宣布一个比VLOOKU ...
- 高德网络定位之“移动WiFi识别”
导读随着时代的发展,近10年来位置产业蓬勃发展,定位能力逐渐从低精度走向高精度,从部分场景走向泛在定位.设备和场景的丰富,使得定位技术和能力也不断的优化更新.定位能力包括GNSS.DR(航迹推算).M ...
- java 金额的大小写转换类
/** *金额大小写转换工具类 */ public class MoneyUtil { /** 大写数字 */ private static final String[] NUMBERS = { &q ...
- TensorFlow笔记-组件
张量 TensorFlow用张量这种数据结构来表示所有的数据.你可以把一个张量想象成一个n维的数组或列表.一个张量有一个静态类型和动态类型的维数.张量可以在图中的节点之间流通.其实张量更代表的就是一种 ...
- C语言入门4-运算符和表达式
一. 分类 C语言一共有34种运算符,10种运算类型,本节我们要掌握的有( 7 种) 算术运算符(+.-.*./.%). 关系运算符(>.>=.==.!=.<.<=). ...
- 从草图绘制到实施交付:优秀API设计完整流程
设计好的API是一项繁复的工作,但是优秀的设计是可以通过人为规划实现的,在本文中,我们将研究什么是好的设计以及如何在开发过程中实现它,还将介绍API设计的三个重要阶段:草图绘制,原型设计和交付实施,最 ...
- python的发展史
python的发展史 1989年,被称为龟叔的Guido在为ABC语言写插件时,产生了写一个简洁又实用的编程语言的想法,并开始着手编写.因为其喜欢Monty Python喜剧团,所以将其命名为pyth ...
- python使用kazoo操作zookeeper时候出现的"kazoo.exceptions.ConnectionLoss"错误
在往zk中写入数据的时候,突然遇到 “kazoo.exceptions.ConnectionLoss“错误,然而对zk链接进行检查,在set之前状态是”CONNECT“. 经过测试后发现是因为写入的字 ...