自己动手写内核系列_skelix
http://www.cppblog.com
http://www.cppblog.com/jinglexy
msn: jinglexy at yahoo dot com dot cn
天衣有缝
2007/6/11
自己动手写内核(序)...................................................................................................................................................................................... 3
第 0 课:环境需求.............................................................................................................................................................................................. 4
第 1 课:引导程序.............................................................................................................................................................................................. 5
第 2 课:保护模式.............................................................................................................................................................................................. 8
第 3 课:辅助函数............................................................................................................................................................................................ 17
第 4 课:中断和异常 1..................................................................................................................................................................................... 25
第 5 课:中断和异常 2..................................................................................................................................................................................... 39
第 6 课:多任务................................................................................................................................................................................................ 45
第 7 课:文件系统............................................................................................................................................................................................ 56
第 8 课:内存管理............................................................................................................................................................................................ 70
第 9 课:系统调用和可执行程序.................................................................................................................................................................... 76
一份免责声明.................................................................................................................................................................................................... 81
自己动手写内核(序)
我对深入研究系统内部非常感兴趣,在大学课程中学习了编译原理相关课程。尝试写过一个编译器,不过它看起来
更像一个汇编器。后来开始使用 FreeBSD,我对它到底怎么实现的感到很好奇,于是找了一份源代码开始阅读,和
你想象的一样,我在数百万行源程序中深陷泥潭。我需要一把来复枪,结果确找到了一堆加农炮:)
后来,我找到了"skelix", 一个 os 内核,运行在 i386 机器上,支持多任务,分页机制,虚拟内存,文件系统等。
这个 tutorial 教你怎样一步一步实现 skelix,虽然它看起来象一个玩具。这样做的好处是看起来更清晰一些。如
果你发现 bug 或好的建议可以告诉原作者(http://www.skelix.org):xiaoming.mo at skelix dot org。译者:
jinglexy at yahoo dot com dot cn(email and msn both),上海体育馆,Kedacom Company。预计 7 月份翻译
完成(2007 年,实际上 6 月份就 finish 了:)。最终整理的文档会做成一个 chm 或 pdf 文件发布在
http://www.cppblog.com/jinglexy,方便读者阅读。如有不当或错误之处希望大家指正,修正后的文档和相关讨
论也发布在这个站点上,源程序也可以在这个博客上下载到,实在不行发邮件到我 msn 上吧。
最近也在准备写一个微内核 os,兼容 posix,跨平台等(很多代码会出自 linux,兼容 linux 驱动)。网络上有了
很好的题材,有兴趣的可以结集一下哦。MSN: jinglexy at yahoo dot com dot cn,qq 群见
http://www.cppblog.com/jinglexy 上面的公告。
晕,这不是广告吗?
目录
第 0 课:环境需求:gcc, vmware,一些资料,风格约定
第 1 课:引导程序:来自黑暗世界的"hello world"
第 2 课:保护模式: That's the first tough one
第 3 课:辅助函数:kprintf, libcc, print_c
第 4 课:中断和异常 1:What a great pleasure to see some error messages.
第 5 课:中断和异常 2:"Hello World!" comes back again
第 6 课:多任务:One bit typo might drive you crazy, believe me
第 7 课:文件系统:A long journey, but not difficult
第 8 课:内存管理:Huge Disappointment
第 9 课:系统调用和可执行程序:Little relief
上海徐汇 jinglexy at yahoo dot com dot cn
第 0 课:环境需求
GCC
Skelix 使用 c 语言编写,当然也用了汇编语言(at&t 风格),在 linux 下使用 gcc 编译。
[root@root ~]$ gcc -v
Reading specs from /usr/lib/gcc/i386-redhat-linux/3.4.2/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info
--enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit
--disable-libunwind-exceptions --enable-java-awt=gtk --host=i386-redhat-linux
Thread model: posix
gcc version 3.4.2 20041017 (Red Hat 3.4.2-6.fc3)
在每篇教程中都给出了源程序和软盘映象文件,你可以直接使用它们。如果你需要编译这些源程序,编译环境必须
正确。我们推荐的环境是 linux2.6.x 内核,gcc3.x 编译器。
由于在源程序中使用了__asm__, __attribute__, __extention__,以及 gcc 内嵌汇编,还有 unsigned long long(直
到 C99 才开始支持);如果你使用了其他编译器,需要修改对应的源程序。且编译器必须是 32 位,这样做的目的是
保持源程序简洁清晰。
对于 windows 用户可以使用 cygwin,它提供了 windows 下的 linux 环境。不过我没有尝试使用它,因为我的电脑
上没有安装 windows 操作系统。也可以在你的 windows 系统上安装一个虚拟机上的 linux,如果你的电脑足够快的
话。
VMWARE
为了运行教程中的范例,一个虚拟机必不可少,virtual pc2007 已经可以免费使用了,在 M$的官方网站上可以找
到下载。当然也可以使用 qemu 和 bochs 之类的虚拟机。推荐的虚拟机是 VMWARE。
Things Are Good To Know
如果能看懂 Makefile 最好了,这是*nix 程序员必须掌握的一项基本知识。另外,如果你熟悉内存地址映射,中断,
异常,GDT,LDT,IDT,分页机制,范围端口就更好了。当然不懂也没关系,Intel 的三卷手册是案头必备:
http://www.intel.com
IA-32 Intel Architecture Software Developer's Manual Volume1: Basic Architecture
IA-32 Intel Architecture Software Developer's Manual Volume2A: Instruction Set Reference: A-M
IA-32 Intel Architecture Software Developer's Manual Volume2B: Instruction Set Reference: N-Z
IA-32 Intel Architecture Software Developer's Manual Volume3A: System Programming Guide Part1
IA-32 Intel Architecture Software Developer's Manual Volume3B: System Programming Guide Part2
读者对这些东西不必紧张,我在教程中会解释相关的知识。c 语言和汇编是最基本的要求,能够很清楚的了解什么
是堆和栈。关于 c 语言的数据成千上万,但是保护模式方面的书籍比哈雷慧星还少,据说每 76 年可以买到一本,
如果你足够幸运的话:)
风格约定
原文中的格式被擅自去掉了,翻译后的风格应该可以一看就懂。
第 1 课:引导程序
目标:使"system"从软盘启动,并打印"Hello World!" 下载源程序
内存寻址
处理器以‘字节’管理和访问内存,每个字节都有独立的地址,即物理地址。有两种地址映射方式:分段和分页,
skelix 内核中都用到了。
段对于我们来说再熟悉不过了,先回顾一下 dos 时期的段吧。它是一个 16 位的寄存器,所以最多可以直接访问 2^16
字节的内存,即 64K。这对应用程序来说太少了,于是 Intel 使用 Segment:Offset 结合方式来表示一个虚拟地址。
段寄存器左移 4 位加上偏移就得到实际的物理地址了。例如,0x7c00:0x0189 表示物理地址 0x7c189,而不是
0x7c000189。计算过程如下:
7C000
+ 0189
-------
7C189
现在我们来计算最大可以访问的地址:FFFF:FFFF
FFFF0
+ FFFF
-------
10FFEF
这个范围是 1M + 65519 bytes, 因为在 80386 中使用了 20 位地址线,所以可以额外多访问 65519 个字节虚拟地址,
例如地址 0x100010 被映射到地址 0x10,访问这两个地址是等价的。
表示同一个物理地址有多种方式,例如 07C0:0000 和 0000:7C00 就是一样的。
另一个概念是线性地址,这个是 32 位地址,只有当分页机制开启时才有效,文章后面会提到它。
引导过程
当系统上电或 RESET 时,处理器将执行一些列的初始化,寄存器被设置成非预知状态,并且 cpu 处于实模式。也许
你想知道 cpu 是怎样设置 segment:offset 为物理地址 FFFF0 的(0xf000:0xfff0 就是 bios 入口地址),这是因为
cs 寄存器有一个非可见部分,它保存了 ffff:0000 地址,并且 cs 在初始化时会被装入 f000 值。此后以正常方式
使用它。当 bois 取得控制权后,根据用户配置(从软驱,硬盘,或 cdrom)中读取第一个 sector 到 00007C00,并
跳转到该地址执行(就是引导程序 bootstrap)。在 bootstrap 中我们可以使用 bios 中断,但是进入 kernel 后就
不能再使用了。
程序一:使用 as 和 ld 的范例
你可以在下载源程序的 01/first.cry/bootsect.s
.text .text 表示代码段
.globl start 表示 start 可以用作外部符号
.code16 GCC 默认使用 32 位地址和操作数,这里告诉它使用 16 位