操作系统开发 - 第一部分






4.39/5 (32投票s)
OS 开发的环境设置
引言
在这个第一个基础教程中,我将展示如何创建一个非常基础的内核,并从软盘启动我们的内核系统。
即使是构建一个简单的“实模式 Hello World”内核也需要大量的工作。在 Windows 环境下使用自己的 OS 内核会有点麻烦,因为大多数文档都假定开发机是 Linux。
我使用 Windows XP,并通过 Microsoft Virtual PC 2007 来测试我的内核。在虚拟机中测试内核可以节省大量时间。在软盘菜单中,我们可以连接或分离软盘镜像。任何长度为 1,474,560 字节的文件都可以用作软盘镜像。Virtual PC 有一个安装了 Windows XP 的虚拟硬盘(实际上 DOS 就能满足我们所有的需求 - 但我安装了 XP,因为我习惯用它)。
有些人喜欢编写自己的引导加载程序。但我更喜欢使用 GRUB 来加载我的内核,这样我就可以立即开始编写内核代码。GRUB 是一个非常好的引导加载程序,支持很多文件系统和文件格式。
GRUB 设置
要设置 GRUB,我们需要 2 个软盘镜像
- boot.img
- helper.img
boot.img 将是我们的启动盘,而另一个用于创建启动盘。
现在我们下载 GRUB 二进制包 grub-0.97-i386-pc.tar.gz 并解压。我们得到 2 个文件,名为 stage1 和 stage2,以及其他文件。
现在我们启动安装了 Windows XP 的 Virtual PC,然后连接 boot.img。格式化软盘,并在软盘根目录下创建一个名为 'system' 的文件夹。将 stage1 和 stage2 文件复制到 system 文件夹。然后分离 boot.img,并连接 helper.img。
现在我们需要将 stage1 和 stage2 文件合并,并将它们作为原始镜像从第一个扇区开始复制到 helper 软盘。我们使用以下 DOS 命令进行合并
>copy /b stage1 + stage2 grub.bin
现在,使用 rawwrite.exe 工具,将 grub.bin 复制到 'helper' 软盘。您可以从许多地方下载此文件 - 有些甚至带有图形界面。
现在关闭 Windows XP,并用我们刚创建的 helper 软盘启动 Virtual PC。要做到这一点,我们必须在启动 Virtual PC 之前连接软盘镜像。出现 grub 命令提示符后,我们分离 helper.img,然后捕获 boot.img,并在命令提示符处输入以下命令。
grub>install (fd0)/system/stage1 (fd0) (fd0)/system/stage2
现在我们已经设置好了启动盘。接下来我们创建简单的内核来测试它。GRUB 加载器需要 multiboot 内核(https://gnu.ac.cn/software/grub/manual/multiboot/multiboot.html)。在这里,我们构建我们的 multiboot 内核。
内核
在这里,我们定义了一个非常简单的 multiboot 内核,它会在屏幕上显示“Hello World”。我们将内核分成两个文件。一个是用汇编编写的,另一个是用 C 编写的。实际上,我们希望尽可能多地使用 C 语言 - 但我们也需要一些汇编。不多。
Kernel.asm 文件
此文件定义了 multiboot 头,并调用 C 源文件中定义的 kernel_main
函数。
[BITS 32]
[global start]
[extern _kernel_main] ; this is in the c file
; ----- Multiboot Header Starts Here -----
MULTIBOOT_PAGE_ALIGN equ 1<<0
MULTIBOOT_MEMORY_INFO equ 1<<1
MULTIBOOT_HEADER_MAGIC equ 0x1BADB002
MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO
CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
; The Multiboot header
align 4
dd MULTIBOOT_HEADER_MAGIC
dd MULTIBOOT_HEADER_FLAGS
dd CHECKSUM
; ----- Multiboot Header Ends Here -----
start:
call _kernel_main
cli ; stop interrupts
hlt ; halt the CPU
为了构建汇编文件,我使用 nasm.exe 工具,它是免费提供的。我更喜欢 ELF 文件格式作为我的内核。
>nasm -f elf kernel.asm -o ks.o
Kernel.c 文件
此文件定义了 clrscr
、printxy
和 kernel_main
函数。
#define WHITE_SPACE 0x07
#define VIDEO_MEMORY 0xb8000
char *videoMemory = (char*) VIDEO_MEMORY;
void clrscr()
{
int i;
for(i=0;i < (80*25*2);i+=2)
{
videoMemory[i]=' ';
videoMemory[i+1]=WHITE_SPACE;
}
}
void printxy(char *message, unsigned int x, unsigned int y)
{
unsigned int i=0;
i=(y*80*2)+x;
while(*message!=0)
{
if(*message=='\n')
{
y++;
i=(y*80*2);
} else {
videoMemory[i++]=*message;
videoMemory[i++]=WHITE_SPACE;
}
*message++;
}
}
kernel_main()
{
clrscr();
printxy("Hello World", 0, 0);
}
为了编译 C 语言文件,我使用 DJGPP,它是 GCC 在 Windows 上的移植版本。它是一个免费工具,您可以在 Google 搜索后下载。我们使用以下命令行进行编译
>gcc -c kernel.c -o kernel.o
链接目标文件
对于链接,我们还需要 binutils 工具来支持 elf。您可以从 这里 下载。
我们首先定义一个链接脚本(link.ld 文件)。它非常重要,因为它有助于将各个部分放置在正确的位置。
OUTPUT_FORMAT("elf32-i386")
ENTRY(start)
phys = 0x00100000;
SECTIONS
{
.text phys : AT(phys)
{
code = .;
*(.text)
*(.rodata)
. = ALIGN(4096);
}
.data : AT(phys + (data - code))
{
data = .;
*(.data)
. = ALIGN(4096);
}
.bss : AT(phys + (bss - code))
{
bss = .;
*(.bss)
. = ALIGN(4096);
}
.rodata : AT(phys + (rodata - code))
{
rodata = .;
*(.rodata)
. = ALIGN(4096);
}
end = .;
}
好的。我们差不多完成了,使用以下命令来构建我们的内核(kernel.bin)。
>ld-elf -T link.ld --oformat elf32-i386 -o kernel.bin ks.o kernel.o
要查看文件是否正确创建,我们可以使用以下命令
>objdump-elf kernel.bin --all
用内核启动
现在我们将 kernel.bin 复制到我们的启动盘的 system 文件夹中,并用该盘启动。当 GRUP 命令提示符出现时,我们输入以下命令
grub>kernel /system/kernel.bin
grub 显示了有关我们的 multiboot-elf 内核的信息。现在我们执行以下命令
grub>boot
voilà - “Hello World”消息显示了。请注意,GRUB 已经设置了保护模式、GDT 和 A20 gate。打印工作的原因是 GRUB 以一种方式设置了 GDT,使得虚拟内存和物理内存到视频内存完全相同。
结论
这是一个非常基础的教程,也是一个非常老的话题。我只是想提供我们开始编写自己的 OS 所需的东西。下一部分可能会涵盖一些高级功能,如保护模式和描述符表,再下一部分可能涵盖更多高级主题。请给我您的评论,以便我改进这篇文章,并在下一篇文章中提供更好的信息。
此外,请注意本文的内容并非我原创。我只是收集了手册和教程并将它们整合在一起。主要目的是建立基本环境。
历史
- 2008 年 6 月 5 日:初次发布