一个操作系统的实现(1)-准备工作

今天开始看《Orange’S:一个操作系统的实现》一书。这里是ubuntu 16.04下开发环境的搭建以及实现一个最小的操作系统(准确地说应该是一个引导扇区)。

工欲善其事,必先利其器。自制一个操作系统需要的工具如下:

  • 汇编编译器NASM
  • 虚拟机算计Bochs
  • 软盘绝对扇区读写工具(这里直接使用强大的dd

现在就来安装这些工具

安装NASM

通过源代码安装,官网下载最新版源代码,当前最新的是nasm-2.12.01版本,下面的内容以此来演示。

个人软件一般安装到/usr/local下,所以压缩包解压到/usr/local

tar zxvf nasm-2.12.01.tar.gz
cd nasm-2.12.01
./configure
make
sudo make install

安装Bochs

首先官网下载最新版源代码。当前最新的是bochs-2.6.8版本,下面的内容以此来演示。

不用ubuntu的包管理软件来安装一方面原因是包管理安装的不一定是最新版,另一方面原因是包管理默认安装的可能没有调试功能。

个人软件一般安装到/usr/local下,所以压缩包解压到/usr/local

接下来按照如下步骤安装:

tar zxvf bochs-2.6.8.tar.gz
cd bochs-2.6.8
./configure --enable-debugger --enable-disasm
make
sudo make install

安装过程中我遇到了如下错误

fatal error: X11/Xlib.h: No such file or directory
fatal error: X11/extensions/Xrandr.h: No such file or directory

这是由于缺少必要的依赖库。网上查找资料得到:第一行依赖于库libx11-dev,第二行依赖于库libxrandr-dev 因此分别安装这两个库即可解决问题:

sudo apt-get install libx11-dev libxrandr-dev

然后继续按照上面的步骤安装即可成功。

工具安装好了,下面通过一个输出`Hello, OS world!`的操作系统来对操作进行演示。 ## 一个简单的源程序 首先必须有源代码:

    org 07c00h
    mov ax, cs
    mov ds, ax
    mov es, ax
    call DispStr
    jmp $
DispStr:
    mov ax, BootMessage
    mov bp, ax
    mov cx, 16
    mov ax, 01301h
    mov bx, 000ch
    mov dl, 0
    int 10h
    ret
BootMessage:    db  "Hello, OS world!"
times   510-($-$$)  db  0
dw  0xaa55

要理解这段代码,首先要会汇编(废话),其次要知道计算机启动操作系统的过程。

当计算机电源被打开时,首先进行加电自检(POST),然后寻找启动盘,如果是选择从软盘启动,计算机就会检查软盘的0面0磁道1扇区,如果发现此扇区以0xAA55结束,那么BIOS就认为它是一个引导扇区。一旦BIOS发现引导扇区,它就会将这512字节的内容装载到内存地址0000:7c00处,然后跳转到0000:7c00处将控制权彻底交给这段引导代码。到此位置,计算机不再由BIOS中固有的程序来控制,而变成操作系统的一部分来控制。

了解了启动过程,再来看上面的程序。

org 07c00horg是一个伪指令,这行代码告诉编译器,将来这段程序要被加载到内存偏移地址0x7c00h处。

mov ax, csmov
ds, ax
mov es, ax:使dses两个段寄存器指向与cs相同的段,以便以后在进行数据操作的时候能定位到正确的位置。

call DispStr:调用子程序DispStr,子程序以ret返回。

int 10h:是BIOS提供关于屏幕和显示器操作的程序。他前面4行都使用来设置参数的。AH=13h表示调用显示字符串的子程序。这时ES:BP = 串地址、CX
= 串长度 、DH, DL = 起始行列、BH = 页号、AL=01h时BL表示显示属性。

jmp $:这里的$表示当前行被汇编后的地址。举个例子,将汇编编译生成的二进制文件反汇编,形成下面的代码:

00007c09    EBFE        jmp short 0x7c09

在这里是实现无线循环的功能。

times 510-($-$$) db 0:这里的$$表示一个节(section)的开始处被汇编后的地址。这个程序只有一个节,所以$$表示的是程序被编译后的起始地址,也就是0x7c00。因此$-$$表示的是本行距离程序开始处的相对距离。这句话的意思是将512字节中剩下的部分赋值为0(最后两个字节除外)。

dw 0xaa55:将最后两个字节赋值为0xaa55,让BIOS把此扇区当作引导扇区,从而将此扇区装入内存0000:7c00处得到执行。

这段代码的大致功能就是这些,接下来就是如何使用Bochs引导并执行它。虚拟机当然不能引导源程序,执行上面的程序首先必须将其编译并制成软盘启动盘。

将源程序编译成可执行文件制作启动盘

使用NASM编译

安装好了NASM,源程序的编译过程一行命令搞定

nasm boot.asm -o boot.bin

这样就得到了一个512字节的boot.bin。接下来需要一个虚拟软盘存放上面的启动程序(放在扇区的0面0磁道1扇区,上面有介绍)

生成虚拟软盘

生成虚拟软盘使用的是Bochs组件中的一个工具:bximage,它既可以生成虚拟软盘,还可以生成虚拟硬盘(虚拟磁盘也被成为磁盘映像)。创建一个软盘映像的过程如下:

$ bximage
?========================================================================
                                bximage
  Disk Image Creation / Conversion / Resize and Commit Tool for Bochs
         $Id: bximage.cc 12690 2015-03-20 18:01:52Z vruppert $
?========================================================================

1.?Create new floppy or hard disk image
2.?Convert hard disk image to other format (mode)
3.?Resize hard disk image
4.?Commit ‘undoable‘ redolog to base image
5.?Disk image info

0.?Quit

Please choose one [0] 1 <<输入1,代表创建一个软盘或硬盘映像

Create image

Do you want to create a floppy disk image or a hard disk image?
Please type hd or fd. [hd] fd <<输入fd,代表软盘

Choose the size of floppy disk image to create, in megabytes.
Please type 160k, 180k, 320k, 360k, 720k, 1.2M, 1.44M, 1.68M, 1.72M, or 2.88M.
 [1.44M] <<直接回车,默认左边内容

What should be the name of the image?
[a.img] <<直接回车,默认左边内容

Creating floppy image ‘a.img‘ with 2880 sectors

The following line should appear in your bochsrc:
  floppya: image="a.img", status=inserted

最后提示你在bochsrc中如何进行配置,到现在为止可执行程序(计算机)好了,虚拟软盘也好了,接下来就是将计算机写入虚拟软盘中了

将引导扇区写进软盘中

linux下使用dd命令:

dd if=boot.bin of=a.img bs=512 count=1 conv=notrunc

其中: `bs=512`:bs是用来规划一个block的大小,如果未指定则默认是512Bytes(一个扇区的大小) `count=1`:多少个bs的意思 `conv=notrunc`:如果不用它的话,软盘映像文件a.img会被截断(truncated),因为boot.bin比a.img小。
现在一切准备就绪了,接下来就是打开计算机电源,调试运行啦。 ## 如何用Bochs进行调试 Bochs是一个虚拟计算机,既然是虚拟计算机,首先要对他进行配置,告诉他你需要的计算机内存是多大的,硬盘映像和软盘映像都是那些文件等等。我的配置如下:

###################################################################
# Configuration file for Bochs
###################################################################

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

# filename of ROM images
romimage: file=/usr/local/bochs-2.6.8/bios/BIOS-bochs-latest, address=0xfffe0000
vgaromimage: file=/usr/local/bochs-2.6.8/bios/VGABIOS-elpin-2.40

# what disk images will be used
floppya: 1_44=/home/zpl/coding/os/a.img, status=inserted

# choose the boot disk
boot: floppy

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

# disable the mouse
mouse: enabled=0

# enable key mapping, using US layout as default
keyboard: type=mf, serial_delay=200, paste_delay=100000

romimagevgaromimage指定的文件对应的其实就是真实机器的BIOS和VGA
BIOS。

floppya指定的是使用哪个文件作文软盘的映像

为了避免错误,上述的文件最好以绝对路径的方式给出

现在只做了最简单的一些配置,详细的配置参考官方手册,讲的很详细。

接下来打开bochs并调试:

$ bochs
?========================================================================
                       Bochs x86 Emulator 2.6.8
                Built from SVN snapshot on May 3, 2015
                  Compiled on May 13 2016 at 04:39:37
?========================================================================
00000000000i[      ] BXSHARE not set. using compile time default ‘/usr/local/share/bochs‘
00000000000i[      ] reading configuration from .bochsrc
?------------------------------
Bochs Configuration: Main Menu
?------------------------------

This is the Bochs Configuration Interface, where you can describe the
machine that you want to simulate.  Bochs has already searched for a
configuration file (typically called bochsrc.txt) and loaded it if it
could be found.  When you are satisfied with the configuration, go
ahead and start the simulation.

You can also start bochs with the -q option to skip these menus.

1.?Restore factory default configuration
2.?Read options from...
3.?Edit options
4.?Save options to...
5.?Restore the Bochs state from...
6.?Begin simulation
7.?Quit now

Please choose one: [6] <<这里选择功能菜单
00000000000i[      ] installing x module as the Bochs GUI
00000000000i[      ] using log file bochsout.txt
Next at t=0
(0) [0x0000fffffff0] f000:fff0 (unk. ctxt): jmpf 0xf000:e05b          ; ea5be000f0
<bochs:1> b 0x7c00<<在内存0x7c00处设置断点
<bochs:2> c<<运行到下一个断点处(此处是0x7c00)
(0) Breakpoint 1, 0x00007c00 in ?? ()
Next at t=29625394
(0) [0x000000007c00] 0000:7c00 (unk. ctxt): mov ax, cs                ; 8cc8
<bochs:3> n<<向下执行一不步
Next at t=29625395
(0) [0x000000007c02] 0000:7c02 (unk. ctxt): mov ds, ax                ; 8ed8
<bochs:4> n
Next at t=29625396
(0) [0x000000007c04] 0000:7c04 (unk. ctxt): mov es, ax                ; 8ec0
<bochs:5> n
Next at t=29625397
(0) [0x000000007c06] 0000:7c06 (unk. ctxt): call .+2 (0x00007c0b)     ; e80200
<bochs:6> trace-reg on<<让Bochs每走一步都显示主要寄存器的值
Register-Tracing enabled for CPU0
<bochs:7> n
Next at t=29626661
eax: 0x00001301 4865
ecx: 0x00090010 589840
edx: 0x00000000 0
ebx: 0x0000000c 12
esp: 0x0000ffd6 65494
ebp: 0x00007c1e 31774
esi: 0x000e0000 917504
edi: 0x0000ffac 65452
eip: 0x00007c09
eflags 0x00000082: id vip vif ac vm rf nt IOPL=0 of df if tf SF zf af pf cf
(0) [0x000000007c09] 0000:7c09 (unk. ctxt): jmp .-2 (0x00007c09)      ; ebfe
<bochs:8> x /64xb 0x7c00<<查看0x7c00开始的64字节内存
[bochs]:
0x00007c00 < bogus+       0> :    0x8c    0xc8    0x8e    0xd8    0x8e    0xc0    0xe8    0x02
0x00007c08 < bogus+       8> :    0x00    0xeb    0xfe    0xb8    0x1e    0x7c    0x89    0xc5
0x00007c10 < bogus+      16> :    0xb9    0x10    0x00    0xb8    0x01    0x13    0xbb    0x0c
0x00007c18 < bogus+      24> :    0x00    0xb2    0x00    0xcd    0x10    0xc3    0x48    0x65
0x00007c20 < bogus+      32> :    0x6c    0x6c    0x6f    0x2c    0x20    0x4f    0x53    0x20
0x00007c28 < bogus+      40> :    0x77    0x6f    0x72    0x6c    0x64    0x21    0x00    0x00
0x00007c30 < bogus+      48> :    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x00007c38 < bogus+      56> :    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
<bochs:9>

在运行到jmp指令时即可出现如下界面:

看,左上角我们写的Hello,OS world!。到这里,一个最简单的操作系统实现出来了。关于Bochs调试的参数,以后详细介绍。

时间: 2024-10-03 17:05:47

一个操作系统的实现(1)-准备工作的相关文章

一个操作系统的实现——笔记4

在2^k*2^k个方格组成的棋盘中,有一个方格被占用,用下图的4种L型骨牌覆盖所有棋盘上的其余所有方格,不能重叠. 代码如下: def chess(tr,tc,pr,pc,size): global mark global table mark+=1 count=mark if size==1: return half=size//2 if pr<tr+half and pc<tc+half: chess(tr,tc,pr,pc,half) else: table[tr+half-1][tc+

《Orange&#39;S:一个操作系统的实现》笔记(一)

感觉自己对于操作系统始终没有一个清楚的概念,尤其最近困扰于实模式.保护模式以及寻址方式等一些概念.转而一想,所有的程序,最终都是操作的计算机资源,需要和操作系统打交道,所以操作系统有必要深入了解一下.最终想要自己动手编写一个简单的版本,上网查.网友对于于渊的<Orange'S:一个操作系统的实现>和<30天自制操作系统>评价挺高的,先选<orange>为学习手册.<30>为参考手册,开始自己的操作系统之旅. 首先是平台的搭建问题,首先因本人编程一般都是在自己

一个操作系统的实现(4)-认识LDT

看到这里,你应该已经很了解GDT了,如果还不了解GDT.请看这篇文章:一个操作系统的实现(2)-认识保护模式,认识保护模式那篇文章的最后详细介绍了由16位寻址升级到32位寻址而引入的GDT. LDT(Local Descriptor Table):从名字上面就可以看出来它与GDT(Gobal Descriptor Table)的区别.GDT是全局描述符表,LDT是局部描述符表(相对于GDT). 下面仍然是从代码的角度讲解什么是LDT.主要讲解在上一节的基础上增加的代码.在文章的最后会附上所有代码

《ORANGE&#39;S一个操作系统的实现》第7章 TTY与键盘输入的关系。

背景:我感觉这块部分有一些逻辑上的复杂,于是我把它的关系结构画了一张图来表述,并且在图上解答了我自己想到的几个问题. 关系如图所示: <ORANGE'S一个操作系统的实现>第7章 TTY与键盘输入的关系. 原文地址:https://www.cnblogs.com/vizdl/p/12178667.html

一个操作系统的实现中jmp dword SelectorCode32:0的理解

; 准备切换到保护模式 mov eax, cr0 or eax, 1 mov cr0, eax ; 真正进入保护模式 jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs, ; 并跳转到 Code32Selector:0 处 这段时间在一个操作系统的实现 好书啊,感谢作者 这两天一直纠结在    ; jmp    dword SelectorCode32:0    ; 这句话,看了些资料,现将自己理解的分享一下,不对的地方,大家指点一

一个操作系统的实现(11)-让操作系统进入保护模式

这节首先介绍了突破引导扇区只有512字节的原理,然后介绍了FAT12文件系统,最后通过实验加载loader并将控制权交给loader来实现突破512字节的束缚. 突破512字节的限制 前面所用的引导扇区只有512字节.然而实际上操作系统在启动过程需要做的事情是很多的.所以需要通过某种方法突破512字节的限制. 那么如何突破512字节的限制呢?一种方法是再建立一个文件,通过引导扇区把它加载到内存,然后把控制权教给它.这样,512字节的束缚就没有了. 这里被引导扇区加载进内存的并不是操作系统的内核.

一个操作系统的实现(8)-进一步体会分页机制

上面的两篇文章中,我们对可用内存进行了统计,并且合理的分配了页表的大小.这节中,我们来看看分页的好处 在此之前不知道你有没有注意过一个细节,如果你写一个程序(在Linux或Windows下均可),并改个名复制一份,然后同时调试,你会发现,从变量地址到寄存器的值,几乎全部都是一样的!而这些"一样的"地址之间完全不会混淆起来,而是各自完成着自己的职责.这就是分页机制的功劳,下面我们就来模拟一下这个效果. 线性地址到物理地址的映射 先执行某个线性地址处的模块,然后通过改变cr3来转换地址映射

一个操作系统的实现(3)-保护模式进阶

上节内容是从实模式进入到保护模式,只是进入保护模式打印了一个字母P.但是没有体现出保护模式的优势,也没有从保护模式中返回.这节就是要体验保护模式下读写大地址内存的能力和从保护模式返回到实模式. 这节要做的内容如下:首先在屏幕的第11行输出In Protect Mode now. ^-^.然后在屏幕第12行输出内存中起始地址为5MB的连续的8个字节.然后向这个以5MB开始的内存中写入ABCDEFGH.再次在第13行输出这8个字节.结果演示如下: 源代码300多行,很长,分段讲述,主要讲新增的部分

《一个操作系统的实现》学习笔记(一) bochs源码安装及配置

前言:本机环境ubuntu 14.04 bochs 2.4.5 一.下载 官网 http://bochs.sourceforge.net/ 二.安装 1.将下载好的压缩包解压并进入该目录 tar vxzf bochs-2.4.5.tar.gz cd bochs-2.4.5 2.安装编译程序依赖的包 sudo apt-get install build-essential 3.检测环境,打开调试功能的开关 ./configure --enable-debugger --enable-disasm