自己动手写操作系统(一)

2023-11-20

本系列文章将一步步实现一个简单的操作系统。实验环境是在Linux系统下通过Bochs虚拟机运行我们自己写的操作系统。

一、实验环境搭建

1. Ubuntu的安装,Windows用户可以选择在虚拟机中安装Ubuntu,具体安装教程可自行搜索。

2. Bochs虚拟机的安装

在学习编写操作系统的过程中,我们需要一个虚拟机来模拟出一个虚拟的计算机硬件环境,比如cpu、内存、硬盘等,并且能够运行并且调试我们写的代码。Bochs很好的提供了以上所有功能,在它面前我们就像上帝一样随时可以让时间停止,”钻“到计算机内部,查看这个虚拟电脑的一切信息,这正是开发操作系统所需要的。

Ubuntu下我们可以直接运行以下命令以源码方式安装Bochs(注意我们之所以选择以源码方式安装,是因为通过apt install安装的Bochs是没有调试功能的)

$ sudo apt update
$ sudo apt install build-essential libx11-dev xorg-dev libgtk2.0-dev
$ wget https://sourceforge.net/projects/bochs/files/bochs/2.7/bochs-2.7.tar.gz
$ tar zxvf bochs-2.7.tar.gz
$ cd bochs-2.7/
$ ./configure --enable-debugger --enable-disasm --enable-debugger-gui
$ make 
$ sudo make install

./configure后面的参数便是打开调试功能的开关

到此实验环境搭建完毕。

二、简单的引导扇区汇编代码

先简单讲一下计算机的启动流程,详细启动过程可参考我的另一篇博客 操作系统启动过程

按下开机键后,计算机首先会运行BIOS中的代码,BIOS在进行硬件检查和初始化后,会按照设置好的启动顺序(我们在使用U盘安装系统时,经常要进入BIOS设置这个启动顺序),依次寻找启动设备(比如硬盘、U盘等)。然后将第一个可用的启动设备的第一个扇区载入内存0x7c00处,并把执行权限交给它。

启动设备的第一个扇区我们称之为引导扇区(MBR),共512个字节,必须以数值0x55及0xaa结尾。包括三部分内容:引导加载程序(Boot Loader)(前446个字节,如GRUB等)、磁盘分区表(DPT,Disk Partition Table)、分区有效性标志(55AA)。其中的引导加载程序负责加载启动硬盘分区中的操作系统。

在BIOS向引导程序移交执行权之前,BIOS会对处理器进行初始化,这其中就包括处理器的代码段寄存器CS和指令指针寄存器IP。当BIOS跳转至引导程序时,CS和IP的值分别为0x0000和0x7c00。此时的处理器处于实模式下,物理地址必须经过CS寄存器和IP寄存器转换才能得到。转换公式为:物理地址=CS<<4+IP,也就是物理地址0x7c00处。

BIOS由Bochs虚拟机提供,我们接下来写的就是这个512字节的引导扇区(MBR)的汇编代码。目前它并不用加载操作系统,我们只让它在屏幕上打印出经典的“hello world”即可。

首先看一下汇编代码:

	org 0x07c00
 	mov ax,cs
	mov ds,ax
	mov es,ax
	mov ax,Message
	mov bp,ax
	mov cx, 13
	mov ax,0x1301
	mov bx,0x0002
	mov dh,0
    mov dl,0
	int 0x10
	jmp $
Message: 
	db "Hello, world!"
    times 510-($-$$) db 0
    dw  0xaa55

代码和数据是按汇编程序的编写顺序依次连续存放到内存的,即上面的程序在0x7c00处开始存放的是org 0x07c00的机器指令,在512字节最后放的是0xaa55数据。

BIOS程序在把引导程序加载到内存时,同时还创建了中断系统,在物理内存的前1KB空间初始化中断向量表,在物理内存最后256KB物理地址空间内保存中断处理程序。cpu运行完BIOS后,物理内存的布局如下:

本程序就是调用0x10号中断,在屏幕上打印字符串。在调用0x10号中断处理程序往显示器的屏幕上打印字符串时,所有的参数都是通过cpu中的寄存器传递的,各参数的含义如下:

  • 寄存器ah:0x13表示向屏幕打印字符串;
  • 寄存器al:指定光标和字符的属性
    0,表示字符的属性值保存在寄存器bl中,光标停留在字符串的首字符
    1,表示字符的属性值保存在寄存器bl中,光标停留在字符串的尾字符
    2,表示字符的属性值紧跟在字符之后,光标停留在字符串的首字符
    3,表示字符的属性值紧跟在字符之后,光标停留在字符串的尾字符
  • 寄存器bl:若寄存器al的值为0或者1时,保存字符的属性值。如图所示,字符属性由一个字节大小的数据表示。
  • 寄存器[es:bp]:保存字符串的首字符在数据段中的逻辑地址
  • 寄存器cx:保存字符串的长度
  • 寄存器dh,dl:字符串在屏幕上的起始坐标,其中寄存器dh为行号,寄存器dl为列号。显示器的屏幕只能显示25行字符,并且每行只能显示80个字符,因此dh的取指范围为0~24,dl的取指范围为0~79。

代码解析:

第1行,告诉编译器程序加载到内存的0x7c00处。

第2~4行,统一数据段寄存器DS和附加段寄存器ES的值和代码段寄存器CS一致,即不论数据段还是代码段,段起始地址都是0x7c00。

第5~6行,将字符串"Hello, world!"的首地址传递给寄存器bp。注意任何不被方括号[ ]括起来的标签或变量名都被认为是地址,访问标签或变量中的内容必须使用[ ]

第7行,将字符串的长度传递给寄存器cx

第8~11行,字符串属性设置

第12行,调用0x10号中断处理程序,在屏幕上显示字符串

第13行,使cpu进入死循环
因为cpu会不停的根据寄存器[cs:ip]中的逻辑地址转换后的物理地址,从物理内存读取机器指令,然后对其解析、执行。其中,当运行完一条机器指令后,cpu自动将该机器指令的下一条机器指令的偏移地址赋值给ip。当cpu运行完可执行文件中的最后一个机器指令后,若不采取任何措施,则cpu会将下面的数据看做机器指令,进行取指、解析、执行。因此,需要一条让cpu进入死循环的机器指令作为可执行文件的最后一条机器指令。

$表示当前行被汇编后的地址,$$表示程序被汇编后的开始地址,也就是0x7c00.

第15、17行,分别以字节和字的形式存放的数据

第16行,表示将0这个字节重复510-($-$$)遍,也就是在剩下的空间不停填充0,直到第510个字节为止。这样加上结束标志0xaa55占用的两个字节,恰好是512个字节。

在运行代码之前我们需要将其转换成计算机能读懂的机器指令形式,这就需要编译器。我们编译c代码使用GCC,编译汇编程序使用nasm编译器。

把上面的代码保存成boot.asm,然后使用nasm编译一下,生成二进制可执行文件boot.bin

$ nasm boot.asm -o boot.bin

三、虚拟硬盘的制作

下面我们将制作一个虚拟硬盘并将已经生成的可执行文件boot.bin放到虚拟硬盘的第一个磁盘块(引导扇区MBR)中。Bochs虚拟机将使用这块“硬盘”引导启动。

首先选择合适的地方创建一个工程目录

$ mkdir projectest
$ cd projectest

将可执行文件boot.bin拷贝到该工程目录中

然后在本层目录中创建一个大小为1MB的硬盘镜像文件b.img的命令如下:

$ dd if=/dev/zero of=b.img bs=512 count=2048

dd是文件拷贝命令,其中:

  • if=/dev/zero:表示拷贝的源文件的路径,"/dev/zero"是一个特殊的文件,可以提供n个0(n的值等于bs和count参数的积)
  • of=b.img:表示拷贝的目标文件路径。若不存在,则创建该文件
  • bs=512:表示块大小,单位为B
  • count=2048:表示拷贝的文件的块的数量。

由bs和count参数可知,硬盘镜像文件的大小为:2048*512B=1MB,硬盘镜像文件制作好后,将可执行文件boot.bin拷贝到硬盘镜像文件b.img(硬盘)的引导扇区的命令如下:

$ dd if=boot.bin of=b.img bs=512 seek=0 conv=notrunc

其中:

  • seek=0:表示把可执行文件boot.bin拷贝到硬盘镜像文件b.img的引导扇区(扇区号为0)。
  • conv=notrunc:表示不改变目标文件的大小,若没有该选项,则硬盘镜像文件b.img的大小会由1MB变为可执行文件boot.bin的大小512B。

这样一个写入了引导程序的“硬盘”就制作好了。

四、Bochs的使用

1. 启动Bochs

"硬盘”制作好后,要想启动bochs还需要一个配置文件——bochsrc.bxrc。为什么需要配置文件呢?因为你需要告诉bochs你希望的虚拟机是什么样的,比如,内存多大,使用哪个硬盘启动等等。在下载bochs的源码包中有一个.bochsrc,就是官方提供的配置文件示例,我们可以根据这个更改。

下面是本实验用到的bochs配置文件代码

romimage: file=/usr/local/share/bochs/BIOS-bochs-latest 
vgaromimage: file=/usr/local/share/bochs/VGABIOS-lgpl-latest 
ata0-master: type-disk, path="b.img"
megs: 16
cpu: count=1
boot: disk

其中:

  • romimage:指定bochs运行过程中使用的ROM-BIOS的路径。
  • vgaromimage:指定bochs运行过程中使用的VGA的ROM-BIOS的路径。
  • ata0-master:指定硬盘镜像文件b.img的路径。
  • megs:指定物理内存的大小,单位为MB。
  • cpu:指定cpu的个数,1个。
  • boot:指定启动方式,从硬盘启动。

将上面的代码保存为bochsrc.bxrc,也存到工程目录下。

现在一切准备就绪,启动bochs的命令如下:

$ bochs -q -f bochsrc.bxrc

其中:

  • -q: 跳过bochs启动后的配置界面。
  • -f : bochsrc.bxrc:指定配置文件的路径。
  • 如果不指定路径,那么Bochs将按照如下顺序在当前目录中寻找配置文件:
  1. .bochsrc

  2. bochsrc

  3. bochsrc.txt

  4. bochsrc.bxrc (windows only)

  5. /home/.bochsrc (Unix only)

  6. /etc/bochsrc(Unix only)

运行bochs后会在终端出现bochs调试命令行,等待我们输入调试命令,这里输入c继续执行
在这里插入图片描述
可以看到在虚拟机中我们的引导程序已成功运行,在屏幕上打印出了hello world。
在这里插入图片描述

2. Bochs调试

常用调试命令 作用
b 使用物理地址打断点
vb 使用逻辑地址打断点
blist 查看所有断点信息
n 单步执行(遇到函数跳过)
s 单步执行(遇到函数进入函数内部)
c 继续执行
r 查看所有通用寄存器的值
(eax、ebx、ecx、edx、esp、ebp、esi、edi、eip、eflags)
sreg 查看所有段寄存器的值
u /5 打印CPU接下来将执行的5条指令
xp 查看物理内存中指定物理地址的内容
xp /2bx 物理地址:打印两个字节,以十六进制格式输出。
xp /13c 物理地址:打印13个字节,以ASCII码对应的字符显示
watch 变量名 运行时,若某一行代码修改了变量,则中断,并打印修改前后的值。
q 退出调试继续执行
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自己动手写操作系统(一) 的相关文章

  • fork之后子进程到底复制了父进程什么

    fork之后子进程到底复制了父进程什么 发表于2015 4 3 9 54 08 2161人阅读 分类 操作系统 include
  • texlive支持中文的简单方法

    1 确保tex文件的编码方式是UTF 8 2 在文档开始处添加一行命令即可 即 usepackage UTF8 ctex 如下所示 documentclass article usepackage UTF8 ctex begin artic
  • Win11微软账号登录不上?Win11登录Microsoft账户出错的解决方法

    Win11微软账号登录不上 近期有部分Win11用户反映在登录微软账号会出现一直转圈 无法登录的情况 这样导致部分功能都不能正常使用了 为此十分令人头疼 那么对于这一情况 有没有什么方法可以有效的解决呢 下面小编教给大家操作方法 大家可以去
  • mapengpeng1999@163.com 操作系统4~处理机调度

    处理机调度 1 三级调度体系 1 处理机调度主要是对处理机运行时间进行分配 即 按照一定算法或策略 将处理机运行时间分配给各个并发进程 同时尽量提高处理机的使用效率 2 现代操作系统中 按调度所实现的功能分3种类型 高级调度 中级调度和低级
  • Client-Server问题

    1 实验内容与要求 需要创建客户Client和服务器Server两个进程 它们通过管道进行通信 Client进程派生3个生产者线程 一个管道线程 共享一个20个slots的缓冲区 每个生产者线程随机产生一个数据 打印出来自己的id 进程 线
  • ps aux 和ps -aux和 ps -ef的选择

    Linux中的ps命令是Process Status的缩写 ps命令用来列出系统中当前运行的那些进程 ps命令列出的是当前那些进程的快照 就是执行ps命令的那个时刻的那些进程 如果想要动态的显示进程信息 就可以使用top命令 要对进程进行监
  • gpuz怎么看显存颗粒

    gpuz可以帮助一些用户查看电脑的一切显卡参数 对于想要了解显卡的网友来说使用起来是非常方便的 不过有些网友是刚开始使用 还不知道gpuz怎么看显存颗粒 下面小编就教下大家gpuz查看显存颗粒的方法 首先 显存颗粒是显存的物理存储组成单元
  • Windows 添加永久静态路由

    route add p 10 10 0 0 mask 255 255 0 0 10 10 6 1 p 参数 p 即 persistent 的意思 p 表示将路由表项永久加入系统注册表
  • 操作系统笔记六(文件管理)

    1 文件逻辑结构 1 1逻辑结构的文件类型 分类 有结构文件 例如 PNG文件 无结构文件 1 2顺序文件 1 3索引文件 2 辅存的存储空间分配 2 1分配方式 连续分配 直接分配连续的存储空间 链接分配 隐式链接 在盘块内指定下一个盘块
  • 《一个操作系统的实现》读书笔记-- 第一章--最小的“操作系统”

    一 最简单的 操作系统 最最简单的 操作系统 就是一个最最简单的引导扇区 Boot Sector 虽然它不具有任何功能 但是它却能够直接在裸机上运行 不依赖其他软件 一个引导扇区是512个字节 并且以0xAA55为结束标识的扇区 下面就是那
  • 程序员的自我修养——链接、装载与库

    1 温故而知新 操作系统概念 北桥 连接高速芯片 系统调用接口 以软件中断的方式提供 如Linux使用0x80号中断作为系统调用接口 多任务系统 进程隔离 设备驱动 直接使用物理内存的弊端 地址空间不隔离 内存使用效率低 程序运行的地址不确
  • Elasticsearch 日志

    下载并安装 Filebeat 首次使用 Filebeat 请参阅入门指南 复制代码片段 curl L O https artifacts elastic co downloads beats filebeat filebeat 7 2 0
  • [架构之路-185]-《软考-系统分析师》-3-操作系统基本原理 - 文件索引表

    目录 一 文件的索引块 二 索引分配表 三 索引表的链接方案 四 多层索引 五 混合索引分配 一 文件的索引块 存放在目录中的文件 并非是文件的真实内容 目录中记录了文件的索引块是几号磁盘块 文件对应的索引表是存放在指定的磁盘块中的 二 索
  • Ubuntu9.04太多乱码(中文不能正常显示)

    最近在使用Ubuntu9 04的过程中 发现有好多地方都出现乱码 其实是中文不能正常显示 现在把我所遇到的所有乱码问题集中一下 方便以后查阅参考 一 Flash乱码 在终端输入 sudo gedit etc fonts conf d 49
  • 内存管理——分页分段

    一 分页存储管理 1 页面与页框 1 页面 将一个进程的逻辑地址空间分成若干个大小相等的片 称为页面或页 并为各页加以编号 2 页框 相应于页面 把内存空间分成和页面相同大小的若干个存储块 称为 物理 块或页框 frame 3 页内碎片 在
  • 《深入理解计算机系统》实验四Architecture Lab

    前言 深入理解计算机系统 实验四Architecture Lab下载和官方文档机翻请看 深入理解计算机系统 实验四Architecture Lab下载和官方文档机翻 我觉得这个文档对整个实验很有帮助 如果你的Y86 64环境还没安装好可以看
  • Common块和Bss段的区别

    昨天看 程序员的自我修养 链接 装载与库 发现不是很理解为什么要用common块 然后仔细看了一番 有了自己的理解 common块 用来存放弱符号 而全局未初始化变量是弱符号 但是难道不是应该存放在 bss段吗 为什么要有common块呢
  • 地址映射与共享

    跟踪地址映射过程 1 通过命令 dbg asm启动调试器 在linux 0 11运行test c文件 使其进入死循环 我们的任务就是找到i的地址并将其修改为0使test c程序退出循环 2 在命令行输入crit c使Boch暂停 一般会显示
  • Linux(13):例行性工作排程

    例行性工程 听谓的排程是将工作安排执行的流程之意 Linux 排程就是透过 crontab 与 at 这两个东西 两种工作排程的方式 一种是例行性的 就是每隔一定的周期要来办的事项 一种是突发性的 就是这次做完以后就没有的那一种 at at
  • 【操作系统xv6】学习记录4-一级页表与二级页表

    占位

随机推荐

  • 使用函数打印无符号整形的二进制表达式

    目录 目录 目录 1 问题描述 输入两个非负整数a b 并输出这两个整数的二进制形式以及这两个数的反码执行逻辑或和逻辑与操作后的二进制形式 2 三个函数作用的详细解释 2 1第一个函数 2 2第二个函数 2 3第三个函数 3 结语 请多多指
  • 一招解决Tomcat闪退

    tomcat的运行需要JRE 一般启动闪退都是因为找不到JRE 也就是说环境安装JDK时环境变量没有配置好 首先检查JDK配置是否正确 确认JDK配置好了以后开始检查错误 在Tomcat的安装目录下的bin文件夹里面找到startup ba
  • 在Windows11系统中安装Anaconda

    1 在电脑自带的应用商店下载或者去Anaconda官网下载 Anaconda官网 2 打开Anaconda官网 如下图 3 点击Download 选择自己电脑对应的版本 这里选择Windows 4 将下载的Anaconda 放在电脑的某个地
  • Python手册(Scientific Computing)--numpy

    文章目录 NumPy的ndarray 创建ndarray ndarrary索引和切片 ndarrary属性 ndarrary方法 numpy函数 NumPy的random随机库 生成n维随机数组 Numba NumPy Numerical
  • 大话数据结构读书笔记 1---线性表

    大话数据结构读书笔记 编程基础 数据结构 算法 1 线性表 顺序储存结构的结构代码 define MAXSIZE 20 储存空间的起始分配量 typedef int ElemType ElemType类型根据实际类型而定 这里假设是int
  • framebuffer驱动详解

    1 framebuffer介绍 1 什么是framebuffer 1 裸机中如何操作LCD 2 OS下操作LCD的难点 显存放在内核中还是应用中是个问题 之前讲的应用和内核之间传递数据用的是copy from usr copy to usr
  • dell灵越笔记本后盖怎么拆_戴尔灵越5584笔记本按键拆卸、安装教程

    最近一直用笔记本 用着用着我发现U键变得迟钝 不灵敏 虽然这是小问题 但对于我的打字造成较大影响 去维修站修又有点浪费 所以就萌生了自己修的念头 发现网上笔记本键帽拆卸的教程不好用 便决定写篇教程 方便他人 第一步 关机 在拆卸笔记本任何部
  • 2023蓝桥杯python 组试题A:2023

    题目 请求出在 12345678 至 98765432 中 有多少个数中完全不包含 2023 完全不包含 2023 是指无论将这个数的哪些数位移除都不能得到 2023 例如 20322175 33220022 都完全不包含 2023 而 2
  • 记录一次生产环境MySQL死锁以及解决思路

    一 背景 1 业务背景 这里因为涉及到公司的业务问题不进行深入讨论 下面换成通用的一些业务场景就是举例 2 技术背景 众所周知 所谓锁的产生本质上是想解决资源竞争问题 在MySQL的前提下 MySQL为了解决事务并发独写的问题 在进行ins
  • Ubuntu查看cuda版本号 cudnn版本号

    cuda版本号 nvcc V nvcc version 若遇到 nvcc command not found 添加环境变量 打开 bashrc 添加环境变量如下 export LD LIBRARY PATH usr local cuda l
  • 常用系统命令

    重定向 cat aa txt gt bbb txt 将输出定向到bbb txt cat aaa txt gt gt bbb txt 输出并追加 查看进程 ps ps ef 显示所有进程 例 ps ef grep mysql 管道符 kill
  • webpack从此不再是我们的痛点 — 核心基础

    webpack一直是前端工程师的痛点 因为他的复杂 分散 loader plugin这些第三方 让我们的学习成本陡然上升 使我们一直对他的配置模棱两可 今天带大家彻底明白他如何配置 摆脱困扰我们很久的痛点 本篇主要是webpack基础配置详
  • Proximal Policy Optimization(PPO)和文本生成

    ChatGPT的RLHF步使用了强化学习PPO算法 PPO是一种策略梯度方法 其交替地进行与环境交互采样数据和使用随机梯度上升优化 代理 目标函数 标准策略梯度方法对每个数据样本执行一次梯度更新 而PPO可以采样一批数据后 对模型进行多次梯
  • 大数据复习笔记——hive

    这次主要讲解一下平常使用较多的数据仓库hive 目录 一 Hive 1 Hive的介绍 2 Hive的搭建模式 a 内嵌Derby模式 b Local方式 c Remote方式 3 Hive的数据库和表操作 a 创建数据库 b 删除数据库
  • mysql-bin.index文件_MySQL 启动报错:File ./mysql-bin.index not found (Errcode: 13)

    Linux下安装初始化完MySQL数据库之后 使用mysqld safe启动mysql数据库 如下发现 启动失败 root SVNServer bin mysqld safe user mysql 或 root SVNServer bin
  • 简单看看很好用的Vite

    前言 Vite对于前端到底需不需要打包提出了自己的观点 在某些情况下他做的确实更好 打包 首先回顾一下什么是打包 从结果导向来看 打包使得开发人员所写的代码和最终运行的代码是不甚相同的两个样子 这样做有什么好处呢 在HTTP2普及之前 浏览
  • 相机开启图像采集与结束图像采集的步骤

    如何开启图像采集线程与关闭该线程 步骤如下 一 按下采集按钮 开始创建一个图像采集线程 ch 按下开始采集按钮 en Click Start button void CBasicDemoDlg OnBnClickedStartGrabbin
  • ASI ‘CFNetwork SSLHandshake failed (-9824)‘

    文章目录 ASI CFNetwork SSLHandshake failed 9824 问题 解决 思考 参考 ASI CFNetwork SSLHandshake failed 9824 10年的老项目 网络库使用是ASIHTTPRequ
  • 微信小程序自动化测试pytest版工具使用方法

    mini https github com zx490336534 pytest mini 微信小程序自动化测试pytest插件 工具 基于MiniTest进行pytest改造 使用方法 准备测试小程序 根据miniprogram demo
  • 自己动手写操作系统(一)

    本系列文章将一步步实现一个简单的操作系统 实验环境是在Linux系统下通过Bochs虚拟机运行我们自己写的操作系统 一 实验环境搭建 1 Ubuntu的安装 Windows用户可以选择在虚拟机中安装Ubuntu 具体安装教程可自行搜索 2