首先阐述下程序运行的基本原理:计算机CPU仅仅运行二进制指令,我们使用的开发语言开发出的程序终于由对应的编译器编译为二进制指令。二进制中包括程序相关的数据、代码指令(用我们最常见的公式描写叙述就是:程序=数据+算法)。CPU读取对应的指令、数据后開始运行,运行后的结果输出到外部设备,如屏幕、磁盘等。整个过程中,CPU发挥最为核心的作用,与其它设备一起完毕程序的运行、输出。

OS本身也是程序。它的执行也是如此,开机后从指定地址处(0x7c00),開始执行指令。

先看看本节样例终于执行效果:

第一个Hello,OS World操作系统_寄存器



编译执行环境:

nasm:Inter x86汇编编译工具。用户将我们的汇编代码编译为二进制。(​​下载地址​​)

Bochs:执行os的虚拟机工具。模拟载入我们生成的软盘映像,并执行os。

(​​下载地址​​)




代码例如以下:

;--------------------------------------------------------------
; 平庸OS(Pf OS) 一个最简单的OS
; Author : tsingfun.com
;--------------------------------------------------------------

; FAT12引导扇区
ORG 0x7c00 ;引导開始地址,固定的,主板BIOS决定,本条指令仅仅是申明,不占字节(有兴趣的能够单独编译这条指令,然后查看二进制,文件0k)
JMP _START ;CPU执行的第一条指令。就是跳转到_START地址处(这里是标签。实际编译后_START是有一个相对地址的)
TIMES 3-($-$$) NOP ;NOP:一个机器周期。$:当前地址,$$:首地址。由于以上信息必须占3个字节。所以不足的部分用nop指令填充,
;详细nop占用几个字节请读者使用二进制查看工具自行验证。

DB "PFOSBEST" ; 标识(公司、品牌等)8个字节
DW 512 ; 每扇区字节数
DB 1 ; 每簇扇区数
DW 1 ; Boot内容占几个扇区
DB 2 ; 共同拥有多少FAT表
DW 224 ; 根文件夹文件数最大值
DW 2880 ; 扇区总数
DB 0xf0 ; 介质描写叙述符
DW 9 ; 每FAT扇区数
DW 18 ; 每磁道扇区数
DW 2 ; 磁头数(面数)
DD 0 ; 隐藏扇区数
DD 2880 ; 若上面“扇区总数”为0。则这个值记录扇区总数
DB 0,0,0x29 ; 中断13的驱动器号;未使用;拓展引导标记
DD 0xffffffff ; 卷序列号
DB "PFOS v1.0.0" ; 卷标(11个字节)
DB "FAT12 " ; 文件系统类型(8个字节)
;---------------------------------------------------------------------
; 448个字节。引导代码、数据及其它填充字符
TIMES 18 DB 0

_START:
MOV AX, 0 ;AX:累加寄存器,CPU内置的16位寄存器,最为经常使用。能够用于存储执行的中间结果,此处清零
MOV SI, MSG ;SI:(source index)源变址寄存器。经常使用来存储待操作的数据的首地址。此处指向数据区的字符串
_LOOP: ;循环指令開始
MOV AL, [SI] ;[]取地址中的内容,AL是AX的低8位,所以取8bit,即1字节,字符串的ASCII。
ADD SI, 1 ;字符串往后移动一个字节
CMP AL, 0 ;推断当前取出的ASCII是否是0,
JE _END ;JE:Equal则Jmp,等于零表示字符串显示完了。结束。
MOV AH, 0x0e ;调用系统10h中断显示ASCII字母。AH,BX指定显示模式及參数(详见:http://www.tsingfun.com/html/2015/dev_0804/570.html)
MOV BX, 15
INT 0x10
JMP _LOOP ;继续下一个字符的显示
_END:
JMP $ ;跳到当前的地址。当然就陷入无限循环啦,此处为了让程序停在此处。

;数据区,就是待输出的字符串信息
MSG DB 0x0a, "----------------------------------------------", 0x0d, 0x0a, \
"| Hello, OS World! |", 0x0d, 0x0a, \
"----------------------------------------------", 0x0d, 0x0a, 0x0

TIMES 510-($-$$) DB 0
DW 0xaa55 ; 结束标志
;----------------------------------------------------------------------

; FAT数据区
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
TIMES 4600 DB 0
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
TIMES 1469432 DB 0

当中。基本的步骤代码中都有详尽的凝视,如有不论什么问题,请移步至论坛《​​深入OS​​》板块发帖讨论。




编译运行过程:

打开dos窗体。进入源代码所在文件夹,运行命令nasm boot.asm -o pfos.img:

第一个Hello,OS World操作系统_寄存器_02

同文件夹下生成一个"pfos.img"软盘映像文件。接下来仅仅须要把它装载到虚拟机执行就可以,当然有条件的话能够实际写入老式的软盘用真机执行,结果是一样的。


同文件夹下新建一个“pfos.bxrc” Bochs配置文件,内容例如以下:

#how much memory the emulated machine will have
megs:4

#filename of ROM images
romimage:file=$BXSHARE\BIOS-bochs-latest,address=Oxf0000
vgaromimage: file=$BXSHARE\VGABIOS-elpin-2.40

#what disk images will be used
floppya:1_44="pfos.IMG",status=inserted

#Choose the boot disk
boot:a

#where do we send log messages?
#log:bochsout.txt

双击“pfos.bxrc”启动Bochs执行就可以启动我们自己写的os了。

源代码​​点此下载​​。




接下来解释一下执行原理:

首先。软盘大小是1.44M(这个是固定的)。所以我们在程序中指定它为1,474,560 字节,除了程序本身的指令、数据外。不足的部分所有补零。

TIMES 1469432  DB 0     就是此处開始写1469432个字节的0。

软盘採用的是FAT12文件格式,我们如今的常见的文件格式有FAT32、NTFS、EXT3等。FAT12是早期的一种文件格式。文件格式是文件格式化存储的一种算法,比方我们要将一个文件存储到软盘(磁盘)上,有些人可能会想我直接从地址0開始存储。直到结束。那么文件名称、文件大小、创建时间等其它信息怎么存?紧接着后面继续存储么?那该给各部分分配多少字节空间?先不说兴许查找文件的效率。这样的存储方法无章可循会全然失控,是不行的方案。

文件格式化算法就攻克了此类问题,并且兼顾文件的高效率查找。

基本原理就是给软盘(磁盘)分区:FAT区、文件夹区、数据区,存储文件时先存储文件基本信息到文件夹区。然后文件的数据依照一定格式存储到数据区,文件夹区中有数据区中文件数据的地址。

这里仅仅简介一下FAT12格式。兴许篇章会深入解析每一个字节代表的含义。

我们来看看我们生成的映像里面究竟有什么东西?这时我们须要用到二进制查看工具WinHex,​​点此下载​​。

第一个Hello,OS World操作系统_数据_03

以上看到的是二进制静态代码。实际执行中各指令的地址都是动态变化的,下来一起借助Bochs的debug功能来一探到底。

我们双击“pfos.bxrc”默认是以执行模式启动Bochs,实际上我们应该启动bochsdbg.exe。因此写个简单的批处理脚本启动它吧。例如以下:

@echo off

SET BXSHARE=C:\Program Files (x86)\Bochs-2.5

if %PROCESSOR_ARCHITECTURE% == x86 (
SET BXSHARE=C:\Program Files\Bochs-2.4.6
)

"%BXSHARE%"\bochsdbg -q -f "pfos.bxrc"


双击脚本,启动debug模式,例如以下:

第一个Hello,OS World操作系统_寄存器_04

Bochs经常使用的debug命令例如以下:

b 0x...   断点命令,指定地址处调试

info break  显示当前断点信息

c    继续运行

s    步入运行

n    单步运行

info cpu   查看cpu寄存器(可分别运行 r/fp/sreg/creg)

print-stack   打印堆栈

xp /长度 地址     显示地址处内容(xp:物理地址,x:线性地址)

u 起始地址 结束地址   反汇编一段代码

trace on    反汇编运行的每条指令

trace-reg on   每运行一条指令都打印一下cpu信息

exit   退出调试

第一个Hello,OS World操作系统_寄存器_05

第一个Hello,OS World操作系统_寄存器_06

大家有兴趣的话能够调试下,然后看看每步骤寄存器值的变化。


总结:本篇主要是让大家对操作系统有个总体概念上的认识。揭开os神奇的面纱。从底层调试到执行,每一个过程都真真切切展如今大家面前。

至于汇编指令、地址寻址临时不懂的话,也不要紧。兴许章节会继续作具体阐述,力求使大家在不断的执行、调试过程中逐渐熟悉并掌握汇编及计算机底层技术。

作者:清泛网,专注IT干货分享。

关注我们的微博:​​http://weibo.com/tsingfun​

官网:​​http://www.tsingfun.com​