跟我一起写操作系统

    返回首页    发表留言
本文作者:李德强
          第三节 制作启动软盘
 
 

        我们使用Eclipse创建一个C语言的Mailfile工程,并创建一个名为boot.S的文件,这个文件就是我们需要使用AT&T汇编语言编写的操作系统引导程序。这个程序非常简单,代码如下:

#ifndef _BOOT_S_
#define _BOOT_S_

      如果没有定义_BOOT_S_标识符,则定义_BOOT_S_,这是一个典型的C语言风格的条件编译处理,目的是为了防止在多个文件相互整体编译时出现的代码定义重复。这里虽然只有一个boot.S文件,但是我们要从一开始就养成良好的编程习惯,所以也要这样定义。

//16位操作数和16位寻址模式
.code16

        我们要在一个32位或64位操作系统的编译环境下编译出一个16位的程序,这样在处理实时模式的CPU才可以识别这样的程序。对于.code16就是定义一个16位操作标识,告诉gcc编译器,我们这个程序从此处开始采用的是16位操作数和16位寻址模式。

//全局过程名
.global _start

        告诉gcc这里有一个叫作_start的过程(也可以理解为C语言的函数),它是一个全局过程,也就是一个外部连接过程。定义一个全局外部过程的目的就是在使用ld命令做链接时需要用到它,这个下面会继续说明。

//数据段
.section .data

        .section .data代表的是数据段开始。

//代码段
.section .text

        .section .text代表的是代码段开始。

//开始 _start: 
jmp _start

       _start:就是定义过程开始的过程名称,jmp start就是当程序执行到这里时,无条件的跳转到_start处,也就是说这里是一个死循环。

//占位,从此行开始到0x1fe止匀为0x90也就是nop
.org 0x1fe, 0x90
        使用0x90占位,机器码为0x90代表的是nop指定,也就是什么都不做。由于一个启动扇区的大小是512个字节(512的16进制表示为0x200),最后两人个字节为0xaa55。所以我们就需要一些占位符来填充中间空白的区域。.org 0x1fe, 0x90的意思是从当前行开始,一直到0x1fe为止,使用0x90来填充这一区域。
//以0xaa55结束
.word 0xaa55
         .word 0xaa55就是定义两个字节0xaa55,也就是这个引导扇区的结束标识符。至此为止一共0x200个字节。
#endif

        #endif结束第一行的#ifndef _BOOT_S_条件编译。

        接下来我们需要将这段汇编代码编译成一个二进制的可执行程序。我们的工程是一个Makefile工程,通过Makefile我们可以灵活的使用gcc、ld、objcop、dd等有用的命令。下面我们来一起看一下Makefile的内容:

BUILD = build
        定义一个变量BUILD,它的值是build,因为我们想用它来作为编译结果的输出目录。
all:
mkdir -p $(BUILD)

        执行Linux的mkdir命令创建一个名为build的文件夹。

gcc boot.S -c -m32 -std=c99 -o $(BUILD)/boot.o
        使用gcc编译boot.S文件,并使用32位编译模式和c99标准。
ld -m elf_i386 -Ttext 0x0 -e _start $(BUILD)/boot.o -o $(BUILD)/boot

        链接boot.o文件,并使用_start过程为程序的入口,这个_start就是我们在boot.S中定义的全局过程名。

objcopy -O binary $(BUILD)/boot $(BUILD)/boot.bin

       将boot.o文件转为纯二进制文件boot.bin。

dd bs=512 count=2880 if=/dev/zero of=$(BUILD)/boot.img

      使用dd命令生成一个boot.img软盘镜像文件,每个块(扇区)大小为512字节,一共有2 * 80 * 18 = 2880个块(2个柱面 * 80个磁道 * 18个扇区)。

dd bs=512 count=1 conv=notrunc if=$(BUILD)/boot.bin of=$(BUILD)/boot.img
        将boot.bin这个纯二进制可执行程序烧录到img软盘镜像文件的第1个扇区中。
clean:
rm -rvf $(BUILD)

        clean为Makefile中一个标识符,意思为清空。在这里是执行rm命令,删除build文件夹。在Eclipse里执行build project命令,或者使用Linux命令在我们的lidqos工程目录下执行make all命令就可以按Makefile的方式进行编译了。

make all 
mkdir -p build
gcc boot.S -c -m32 -std=c99 -o build/boot.o
ld -m elf_i386 -Ttext 0x0 -e _start build/boot.o -o build/boot
objcopy -O binary build/boot build/boot.bin
dd bs=512 count=2880 if=/dev/zero of=build/boot.img
dd bs=512 count=1 conv=notrunc if=build/boot.bin of=build/boot.img
2880+0 records in
2880+0 records out
1474560 bytes (1.5 MB) copied, 0.148839 s, 9.9 MB/s
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.00113384 s, 452 kB/s
17:17:09 Build Finished (took 534ms)

        编译成功之后在我们的工程目录下就会生成boot.o、boot、boot.bin、boot.img等文件,如下图所示:



 

        这样,我们就有了一个可以“引导”计算机的操作系统程序,虽然这个程序什么功能都没有,只有一个死循环,但毕竟在启动计算机时就可以让BIOS程序找到它,并且不再会出现类似“Could not read from the boot mediaum!”的错误了。

        使用VirtualBox创建一个名为lidqos的虚拟机,为其添加一个软盘控制器Floppy,并为其分配我们刚刚生成的boot.img文件的全路径。如下图:



 

        点击OK按钮之后,启动lidqos虚拟机,就可以看到我们的操作系统了。如下图所示:


 

        运程的效果是我们预期的,因为我们的操作系统始终在执行那个永无休止的循环。

        源代码的下载地址为:

https            https://github.com/magicworldos/lidqos.git
git              git@github.com:magicworldos/lidqos.git
subverion        https://github.com/magicworldos/lidqos
branch           v0.1

 

    返回首页    返回顶部
#1楼  Five  于 2016年12月02日22:57:59 发表
 
这里是否需要使用交叉编译环境,如果我测试机是个arm的平台,是否就需要arm-gcc来编译,谢谢!
#2楼  李德强  于 2016年12月05日09:18:12 发表
 
感谢您的支持!这个内核程序是不能使用交叉编译的,因为这是一个直接运行在硬件层面上的操作系统内核程序,所以编译出的程序实际上是“纯机器码”。也就是说如果你需要在arm平台上运行,就需要针对arm的CPU指令集编写代码。我们这个程序是适用于X86型CPU指令集。
  看不清?点击刷新

 

  Copyright © 2015-2018 问渠网 辽ICP备15013245号