我是如何学习写一个操作系统(二):操作系统的启动之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代码方式来 ...
随机推荐
- 个人永久性免费-Excel催化剂功能第44波-可见区域复制粘贴不覆盖隐藏内容
Excel的复制粘贴操作,每天都在进行,若其中稍能提升一点效率,长久来说,实在是很可观的效率提升. Excel自带的复制粘贴功能,若复制的数据源或粘贴的目标位置中有隐藏的行列内容,简单一个复制粘贴充满 ...
- Codeforces比赛注意事项(英语比较好,能翻译题目的可以跳过此文章)
由题目可知,这篇文章是讲翻译文章的. 当然不是教英语啦 其实cf的比赛对于本蒟蒻最大的挑战就是翻译题目啦 所以我比赛时只能靠各种翻译器去无耻地翻译,然后读中文. 目前较好的翻译器有:百度,谷歌,有道. ...
- linux初学者-DDNS配置篇
linux初学者-DDNS配置篇 如果DNS服务器要记录多台主机的IP,且这些主机的IP都是通过DHCPD服务自动获取的,那么将会造成很大的困难,因为在DNS设置时无法得知主机具体的IP.如果DHCP ...
- 图像相似度中的Hash算法
度量两张图片的相似度有许多算法,本文讲介绍工程领域中最常用的图片相似度算法之一--Hash算法.Hash算法准确的说有三种,分别为平均哈希算法(aHash).感知哈希算法你(pHash) ...
- handlerAdapter与方法调用(参数的解析)
前提:当找到handler以后,那么就要让handler发挥作用,这个时候handlerAdapter就派上用场了 这里面比较复杂就是requestMappingHandlerAdapter了,其他的 ...
- C++学习之路
一.二分查找 1.binary_search:查找某个元素是否出现. a.函数模板:binary_search(arr,arr+size ,indx) b.参数说明: arr: 数组首地址 size: ...
- 【JDK】JDK源码分析-Map
Map 接口 Map 是一个接口,它表示一种“键-值(key-value)”映射的对象(Entry),其中键是不重复的(值可以重复),且最多映射到一个值(可以理解为“映射”或者“字典”). Map 常 ...
- maven项目引用错误 和项目结构问题
解决办法: 鼠标右键 maven ---->update prroject Configuration 然后 maven clean maven install
- gradle脚本源码查看环境搭建
背景 我刚入门学习gradle时,网上资料都是说通过gradle的api查看并学习脚本编写,但是api一般只有接口说明,并不能深入了解各个api的实现逻辑,有时就会对一些脚本的写法感到疑惑.通过搭建源 ...
- 自练Eclipse搭建SSH全自动注解博客项目笔记
1.创建一个动态的java项目 2.导入搭建所需要的jar包 3.配置web.xml文件 1).头文件 2).struts2的拦截器 3).定位加载Spring容器的配置文件 4).监听 5). 6) ...