我是如何学习写一个操作系统(二):操作系统的启动之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代码方式来 ...
随机推荐
- Scrum 使用絮叨
关于Scrum 的一些絮叨 Scrum 的推行的基础在于全员参与,全员协作,包含Dev, QA(quality assurance) ,BA(Business analyst)以及Supporter ...
- 关于vue项目font字体图标库导入未显示的问题
运行项目时,弹出以下信息:
- [leetcode] 263. Ugly Number (easy)
只要存在一种因数分解后,其因子是2,3,5中的一种或多种,就算是ugly数字. 思路: 以2/3/5作为除数除后,最后结果等于1的就是ugly数字 Runtime: 4 ms, faster than ...
- Docker 工作原理及容器化简易指南
Docker 非常棒! 它使软件开发者无需担心配置和依赖性,在任何地方打包,发送和运行他们的应用程序.而在与 Kubernetes 相结合后,它使应用集群部署和管理变得更方便.这使得 Docker 深 ...
- C/C++用new、delete分配回收堆中空间
int *CreateList() 10 { 11 int a[5]; 12 int *a = new int[5]; 13 delete[] a; 14 15 int a(5); 16 int a ...
- 为 Editor.md 编辑器插件增加预览和发布按钮
前言 一直在使用 Editor.md 插件作为博客的编辑器,用着挺好,但是在全屏下编辑时,每次想预览或者保存又必须切换到非全屏状态下才可以点击按钮,用着不舒服,所以花了一点时间在工具栏上增加了预览.保 ...
- Linux设备驱动程序学习----1.设备驱动程序简介
设备驱动程序简介 更多内容请参考Linux设备驱动程序学习----目录 1. 简介 Linux系统的优点是,系统内部实现细节对所有人都是公开的.Linux内核由大量复杂的代码组成,设备驱动程序可以 ...
- spring中获取容器中的Bean为什么前转成接口而不是实现类
简单介绍一下上下文,userService是服务层接口有一个save方法,userServiceImpl是该接口的实现类重写了save方法. applicationContext.xml如图: 后台代 ...
- 基于TP5.1实用案例及教程
推荐<基于TP5.1实用案例及教程>书 目录: 通用封装 Export通用封装Import通用封装配合Import通用封装的ImportBaseVerify类Files通用封装Direct ...
- zabbix监控WEB网站性能
一直在纠结用什么实例来给大家演示呢?想来想去还是官方的好,那我们怎么用zabbix监控web性能和可用性呢?我们这边分为几个步骤:打开网站.登陆.登陆验证.退出,一共4个小step,看实例. 检测流程 ...