另外一篇自己动手写操作系统(转贴)

WiseDO 2003-12-27 03:13:24
在哈工大纯C论坛论坛有找到一篇写操作系统基础知识的文章,转贴供参考。
哈工大纯C论坛
http://software.hit.edu.cn/borlandclub/bbs/
前几天转贴的一篇类似但不同的《自己动手写操作系统》在这里:
http://expert.csdn.net/Expert/topic/2602/2602399.xml?temp=.6439173


哈工大纯C论坛
操作系统实验
When Do We Write Our Chinese OS ?

操作系统是计算机的核心,没有操作系统,一切计算机应用都免谈,但现在操作系统基本上被老外垄断,Windows就不说了,就算是Linux那也是泊来品,什么时候我们才能写出有中国特色的操作系统啊?

在工大,我们每个人都学过操作系统,我也一样,但老师教的那真的只是理论,一个实际的系统原比老师教的要复杂上一千万倍!然而,我们基本上没有可能实践的机会,就算是有一些实验,那也是停留在一个非常高的表层。我非常之笨,学完之后,还是不知系统是怎样从无到有,开始工作的。系统是怎样启动的?曾经把一段汇编代码写进了磁盘,但无论如何没将机器启动起来(原因现在已经清楚,稍后再谈)。想看看Linux的源码,但犹如天书!即使是Minix也非常之庞大,晦涉难懂!郁闷啊~~~,今天在网上无意间看见了一个老外写的E文,讲到此事,霍然开朗,在网上狂找了一堆E文后,最终将机器起启来了。(本人E文差得没底,基本上是用金山词霸配合联通国际在线翻译系统一句一句翻译的,痛苦啊!看来要想学好计算机E文不好还真不行:(,很多资料中文的就是没有,只有E文的,你咋办?)。现特将全过程描述一下,一来留个纪念,二来希望工大能有更多的人能对此感兴趣,超级大牛们早日写出我们自己的操作系统。Linux也是芬兰的一个大学生写的。

阅读本文最好有那么一丁点的汇编基础。另外,本人水平极其有限,对操作系统也不是很熟,对于论述中不计其数的错误,望大家原谅,千万不要来砍我~~~
首先,我要先说明一下计算机在你按下电源按钮后,计算机都做了什么
当你按下电源键的时候,同这个键相联的电线就会送出一个电信号给主板,主板将此电信号传给供电系统,供电系统开始工作,为整个系统供电,并送出一个电信号给BIOS,通知BIOS供电系统已经准备完毕。随后BIOS启动一个程序,进行主机自检,主机自检主要工作是确保系统的每一个部分都得到了电源支持,内存储器,主板上的其它芯片,键盘,鼠标,磁盘控制器及一些I/O端口正常可用,此后,自检程序将控制权还给BIOS。

接下来,BIOS开始启动操作系统。
BIOS将访问启动盘的第一个扇区(0磁道,1扇区,一共是512字节),BIOS将这第一扇区中的内容调入内存的0x7c00地址处,并开始执行它。这是启动系统的第一关,从此之后,系统就将控制权将给操作系统了,留下的事情就由你的程序来完成。

现在我们的任务就是写这样一个程序,系统将它称之为引导程序,用它来引导或说启动我们的计算机。它有如下两个特点:
1。大小只能是512字节,不能多一字节,也不能少一字节。因为BIOS只能读512字节的数据到内存中,多的部份BIOS不会理采
2。它必须以"55 AA"结尾,即最后两字节(511,512)必须是它们。这是引导区程序结束的标志,没有它BIOS不会将它作为引导程序看待。(我以前的程序没有执行,就是因为没有在这里写"55 AA")
把这一程序放在磁盘的0磁道,1扇区里,这样,此磁盘就可以用来引导系统,而且是用的你自己做的引导程序!

在开始制作引导程序之前,先介绍一下怎样在Windows环境下进行这样的开发。

首先,需要一个实验环境,你当然可以就用真实的计算机,如果你有多台计算机的话,且不觉得麻烦的话。
这里我们使用虚拟机来进行实验,它与使用真实的计算机是一样的,不信待会儿你可以自己实验一下。
我用的是 MS Virtual PC,使用非常简单,这里就不多说了,它可以用一个1.44M大小的img文件,作为模拟软盘,因此,我们就只需把我们的引导程序写到一个img文件中,就如同写在了一张磁盘上面,就可以用它来引导系统。
启动虚拟机后,在Floopy菜单下,选Floopy Disk Image项,然后选到我们生成的那个img文件后,就可以了。

下面说说img文件的创建生成方法
要把引导程序写到这个1.44M的文件里面,我使用的是WinHex工具,它非常方便,可以直接通过拷贝完成二进制文件的写入,而且还可以创建指定大小的文件。
1.44M的img文件可以用WinHex来创建,点击新建按钮就行,大小输入1474560,单位字节。
到时后,把我们写的程序用WinHex打开,将其内容复制到生成的img文件下就行。

这些工具连同本文所介绍的实验程序,我已经打包,大家可以通过下面的地址下载
ftp://202.118.239.46/Incoming/Other/BTC/temp/os_test/os_test.rar

下面再介绍一个本程序用到的唯一一个BIOS中断,
Int 0x10
0x10 中断是BIOS的显示器中断,所有输出都需要调用此中断,在使用前你需要设置一部分寄存器的值以告诉BIOS怎样进行输出
ah : 0x0e 打字机模式,告诉BIOS,把字符输出到屏幕上
bh : 页码
bl : 文字属性
al : 欲现示的字符的ASCII码

好了,下面我们就能创建我们的引导程序,完整的源程序如下:

[BITS 16] ; 告诉编译器,编译成16位的程序
[ORG 0x7C00] ; 告诉编译器,代码将从0x7c00处开始执行

main: ; 主程序
mov ax , 0x0000 ; 以下两句设置数据段为0000
mov ds , ax
mov si , Message ; 设置基址指针
call ShowMessage ; 调用显示函数
jmp $ ; $ 代表此语句的地址,表示在此语句此进行无限循环

ShowMessage: ; 显示函数
mov ah , 0x0e ; 设置现示模式
mov bh , 0x00 ; 设置页码
mov bl , 0x07 ; 设置字体属性

nextchar:
lodsb ; 字符载入指令
; 它将DS数据段中SI为偏移地址的源串中的一个字符取出送AL,同时修改SI指向下一个字符
or al , al ; 测试字符串是否为0
jz return ; 如果为零则表明字符串结束,跳转到返回指令处返回原调用函数
int 0x10 ; 调用BIOS 10号中断显示字符
jmp nextchar ; 继续显示下一下字符
return:
ret ; 返回原调用函数

Message:
db 'Can We Write A Chinese OS ?'; 定义显示消息
db 13 , 10 , 0 ; 13 表示回车,10 表示换到下一行 0 表示字符串结束

times 510 - ( $ - $$ ) db 0 ; 填充 0 以满足文件大小足够512字节
; $ 表示当前语句的地址,$$ 表示程序的起始地址
db 0x55 , 0xaa ; 结束标志

用任何一个文件编辑器输入它之后,存为test.asm
然后用一个汇编程序编译它,我用的是nasm,
输入 nasm test.asm
编译完成后,会生成一个 test 文件,用WinHex打开它,然后照上面讲的方法,把它写到一个img文件中,你就可以尝试一下用你自己的程序启动计算机了。
也可以用WinHex将它直接写到磁盘上,通过磁盘启动,这样的感觉非常爽啊~~~

下面是它的运行结果

此主题相关图片如下:

一个操作系统非常的复杂,这里只是我的一个尝试实验的心得,它只完成了系统启动这一步,严格说来引导程序不算是操作系统,虽然它无比重要,一般来说,它需要把真正的操作系统的内核载入内存,然后用一条jmp指令跳转到真正的内核处执行。

抛砖引玉,希望有人能早日做出Chinese内核的,拥有自主知识产权的实用性操作系统。
错误不计其数,希望各位老师大侠牛人不吝指点,同好者共同探讨研究学习~~~
留个mail: _xiao_han_@126.com ^_^
...全文
3105 43 打赏 收藏 转发到动态 举报
写回复
用AI写文章
43 条回复
切换为时间正序
请发表友善的回复…
发表回复
das 2004-01-11
  • 打赏
  • 举报
回复
呵呵,快看不懂了。
zhp831221 2004-01-11
  • 打赏
  • 举报
回复
大家应该为中国IT的发展多多努力啊?争取早日有自己的操作系统
WindyLotus 2004-01-11
  • 打赏
  • 举报
回复
up
xupan007 2004-01-11
  • 打赏
  • 举报
回复
做出来也没市场!!!!!!!!!!!!


=======================

难道说没市场就不能做了吗

那你过去学习的时侯编的那些练习程序不也没市场吗???
学习嘛,说那么多干嘛,你不想做,有人想。
xupan007 2004-01-11
  • 打赏
  • 举报
回复
up
solar 2004-01-07
  • 打赏
  • 举报
回复
mark
allen_wang 2004-01-07
  • 打赏
  • 举报
回复
对,嵌入式os内核大小往往100k左右就可以了。而其他的诸如网络系统、文件系统、n多n多的设备驱动则是很烦人的。
ppgg1979 2004-01-07
  • 打赏
  • 举报
回复
国内早就有公司作出嵌入式操作系统来了,只不过是应用在军工领域罢了。另外我也觉得技术上不存在大的困难,内核部分相对来说还容易些,操作系统源代码大部分篇幅都是在诸如IO系统,文件系统等等上头了
allen_wang 2004-01-07
  • 打赏
  • 举报
回复
楼上的说的不对了,国内还是可以做出来的,只是要看做到什么样的程度罢了。
这方面我们的技术积累不是很多,需要很多人力物力的投入,一般的公司也没有这个魄力去做。国家投钱大多是投到高校的实验室里面。但是实验室最大的特点是人员流动性很大,研究生3年换一次代,刚刚有点心得就要毕业了。
所以我觉得主要还是条件的问题。技术上没什么大的困难。
feup 2004-01-07
  • 打赏
  • 举报
回复
做出来也没市场!!!!!!!!!!!!
allen_wang 2004-01-07
  • 打赏
  • 举报
回复
是的,国内普遍弥漫着一种浮躁的气氛。
导师忙着拉项目赚钱,不管研究,不管技术创新。
学生呢,更是赶潮流,什么热门学什么,什么赚钱学什么,什么容易学什么,到头来是什么都没有学到。
也不知道这到底是谁的责任,教育制度真的需要好好改改了。
minitigger 2004-01-07
  • 打赏
  • 举报
回复
国内自主操作系统开发的局限性表现在很多方面。
首先,技术上不成熟,积累的不够。而且现在的国内没有这种让你埋头作的环境。
其次,从市场上来讲,即使技术没有问题了,市场也很难,你首先的说服应用程序开发商用你的操作系统,进行基于你的操作系统的开发,也就是你要对应用系统集成商开放一部分。鉴于中国的国情,没有等到你出正版的,盗版的已经铺天盖地了。
还有楼上讲的所谓的实验室的人员流动性大,这个好像不是主要问题吧?看看现在的大学实验室里面,有几个实在踏踏实实的高学术的,大多还是再搞权术而已,用学生来做课题,来写论文归于自己名下。我看首先的搞个奖惩机制,把那些什么教授啊,助教之类的积极性提高上去再说。比较一下,就可以看出差距,国内的实验室和国外的实验室。
lw549 2004-01-07
  • 打赏
  • 举报
回复
收藏
52study 2004-01-02
  • 打赏
  • 举报
回复
很好,我喜欢
minitigger 2004-01-02
  • 打赏
  • 举报
回复
操作系统,我不觉得现在国内能做出来,不用说个人了。
你们现在做的这一部分只能是一个boot和shell,还没有核心的东西。万里长征才走了一步。
另外更正一下。现在的操作系统的书,得却没有讲boot的事情,讲的都是理论上的系统内核的东西。如果想做操作系统,这些书还是的好好精读一下了。
52study 2004-01-02
  • 打赏
  • 举报
回复
maker
WiseDO 2004-01-02
  • 打赏
  • 举报
回复
== csdn每次可发布的字数有限,也不允许连续回复三次以上,还剩一段今天贴上:) ==
(转贴)

下面,我们将进入主题,将讲述一下怎样处理键盘输入。
处理键盘输入有两种方式,一是通过循环就行,在主程序中不断的查询0x60端口是否有数
据,如果有数据则表示有键盘输入,且此数据就是输入的键的键盘扫描码,将扫描码转换
成相应的ASCII码,然后显示就行。 这种情况非常简单,我们就不详细描述了,你可以改
动本源程序用此种方式实现。这里,我们常用一种新的方式进行,这就是通过中断进行。

要使用中断方式,我们就必须编写自己的中断处理程序,并且要让CPU知道此中断的中断处
理程序在什么地方,这通过IDT(中断描述符表)完成。此表的每一个表项对应一个中断,
每一个表项都指明此中断的中断处理程序在什么地方。因此首要的任务是要构造一个中断
描述符表。

中断描述符表一共可有256项,即256个中断。头三十二项,也就是0~31号中断,已经被CPU
及硬件所占用了,因此我们需要从第三十三项即32号中断开始构造我们自己的中断及中断
服务程序
中断描述符每项占8个字节(64位),所以我们定义如下的一个结构来处理它:
typedef struct{
unsigned long dword0 ;
unsigned long dword1 ;
} segment_desc ;
下面是我们定义中断描述符表的程序:
segment_desc idt[ 256 ] ; /* 中断号 0~255 */
unsigned long idt_desc[ 2 ] ;
unsigned long idt_addr ;
unsigned long keyboard_addr ;
unsigned long idt_offset = 0x8 ; /* IDT 在 GDT 中的位置,此程序中也是代码段在
GDT中的位置 */

// 发送4个ICW
ToPort( 0x20 , 0x11 ) ;
ToPort( 0xA0 , 0x11 ) ;
ToPort( 0x21 , 0x20 ) ;
ToPort( 0xA1 , 0x28 ) ;

ToPort( 0x21 , 0x4 ) ; // 在 Inter 出产的硬件中,PIC之间的联系是
ToPort( 0xA1 , 0x2 ) ; // 把 PIC1的IRQ2 同 PIC2 的IRQ1 联系起来

ToPort( 0x21 , 0x1 ) ;
ToPort( 0xA1 , 0x1 ) ;

// 下面设定中断屏蔽字,只许可键盘中断
ToPort( 0x21 , 0xFD ) ;
ToPort( 0xA1 , 0xFF ) ;

keyboard_addr = ( unsigned long )keyboard_interrupt ; /* 键盘中断处理程序
的位置 */
idt[ 0x21 ].dword0 = ( keyboard_addr & 0xffff ) | ( idt_offset << 16 ) ;
idt[ 0x21 ].dword1 = ( keyboard_addr & 0xffff0000 ) | 0x8e00 ;

/* 得到整个IDT的位置描述 */
idt_addr = ( unsigned long )idt ;
idt_desc[ 0 ] = 0x800 + ( ( idt_addr & 0xffff ) << 16 ) ;
idt_desc[ 1 ] = idt_addr >> 16 ;

__asm__( "lidt %0\n""sti" : "=m"( idt_desc ) ) ; /* 用lidt指令载入 IDT 表,
并用 sti 指令开中断*/
下面我们来解释一下这个程序
ToPort是程序定义的一个函数,具体代码请见源程序,这里只需要知道,它把由第二个参
数指定的数据,发到由第一个参数指定的端口中去。
先来看看这两行
idt[ 0x21 ].dword0 = ( keyboard_addr & 0xffff ) | ( idt_offset << 16 ) ;
idt[ 0x21 ].dword1 = ( keyboard_addr & 0xffff0000 ) | 0x8e00 ;
一个 IDT 表项有64位长,0~15位是中断处理程序地址的低16位,16~31是中断处理程序所
在的段在GDT中的位置(参见第二篇)。
最高的16位是中断处理程序地址的高16位,而留下的中间的16位是用来表明此是一个中断
门还是一个陷阱门还是一个任务门,及是16位中断还是32位中断等,非常复杂,要想详细
了解请查看Intel CPU 开发人员手册(网上有下的,没找到的可以找我要)。幸运的是,
我们不需要管得太多,只需记住在正常情况下是置为0x8e00就行。

下面我们详细讲一下代码中的“// 发送4个ICW”部份

我们现在已经知道,要建立中断,我们需要填充 IDT( 中断描述符表),它需要指出当发
生中断时,应跳到哪儿去执行。

为了使中断系统起作用,我们需要对PIC(可编程的中断控制器)进行编程,PIC 是可编程
的中断控制器,它可以处理硬件中断请求(IRQ0,IRQ1..等等),如果没有PIC,我们就不得
不按规则去查询哪一个硬件发生了中断,有了PIC,当硬件发生中断时,PIC把中断信号送
到CPU,CPU处理中断。我们实际上有两上PIC,第一个PIC1(端口号0x20~0x21)处理IRQ0~IR
Q7的请求,第二个PIC2(端口号0xA0~0xA1)处理 IRQ8~IRQ15 的请求

CPU只知道逻辑意义上的中断,不区分是物理上的软件中断还是硬件中断,因此我们必须把
CPU不知道的物理中断,映射为CPU知道的逻辑意义上的中断。在实模式下,这项工作由BIO
S来做,在保护模式下需要我们自己做。

因此我们需要初使化PICs,这通过发送一些ICW(初始化命令字)来实现对PICs的控制,它
们必须被精确的依次序发送,因为,它们之间是相互依赖的
1. 发送 ICW1 到 PIC1(20h) 与 PIC2(A0h) 中
2. 发送 ICW2 到 PIC1(21h) 与 PIC2(A1h) 中
3. 发送 ICW3 到 PIC1(21h) 与 PIC2(A1h)中
4. 发送 ICW4 到 PIC1(21h) 与 PIC2(A1h)中

ICW1 用来告诉PIC, 存在ICW4,(当两个PIC串联工作时,这是必须的)
ICW2 用来告诉PIC,把 IRQ0 与 IRQ7 映射到什么地方
(每一个PIC有八个管脚处理中断(IRQ0~IRQ7)
ICW3 用来告诉PIC,它们之间应当用几号IRQ(第几根管脚)进行同信
ICW4 用来告诉PIC,我们工作在保护模式,并且是由软件来处理还是自动处理中断

ICW1的结构
7 6 5 4 3 2 1 0
0 0 0 1 M 0 C I

I : 如果 ICW3 后面还有 ICW4,则置位
C : 如果不置位,表明这两个 PIC 工作在串联状态下
M : 表明 IR0 到 IR7 的线是水平触发,在PC机中,这位应为0(边沿触发)

ICW2 表明了 IRQ0 在中断向量表中的地址,在保护模式下,你应当改变它
7 6 5 4 3 2 1 0
A7 A6 A5 A4 A3 0 0 0

IRQ1 在中断向量表中的地址为 IRQ0的+1,IRQ2~IRQ7以此类推

ICW3 只在 这两个PIC是在串联工作状态下才被发送(ICW1 的C位置0),它的目的是在两
个PIC间建立联系

ICW3 关于 PIC1 的结构
7 6 5 4 3 2 1 0
IR7 IR6 IR5 IR4 IR3 IR2 IR1 IR0

如果 IR0 是置0的,则表明此根线联到一个外围设备
如果 IR0 是置1的,则表明此根线与PIC2联结
其余的与此类似

ICW3 关于 PIC2 的结构
7 6 5 4 3 2 1 0
0 0 0 0 I R Q
最后3位是PIC1的,与PIC2相联结的IRQ号

ICW4 的结构
7 6 5 4 3 2 1 0
0 0 0 0 0 0 EOI 80x86
EOI 表明中断的最后是自动处理还是由软件辅助处理。在PC中此位通常置0,表示软件必须
处理中断的最后扫尾工作
80x86 表明PIC是否工作在80x86的体系结构下

有了上述基础知识,你现在应当可以理解了吧。
下面我们再来看看:中断屏蔽字
PIC 1 处理的中断有
0 系统时钟
1 键盘
2 重定向到IRQ9 (PIC2的IRQ1),如果此位被置1,则屏幕掉所有来自PIC2的中断
3 串口 1 (COM2/4)
4 串口 2 (COM1/3)
5 声卡
6 软驱
7 并行端口

PIC 2 处理的中断有
0 实时时钟
1 来自 IRQ2 (PIC1)
2 保留
3 保留
4 鼠标
5 数学协处理器
6 硬盘
7 保留

当某位置位0表示允许其发出中断请求,置为1表示屏蔽其中断请救
程序中,有这样两行代码:
ToPort( 0x21 , 0xFD ) ;
ToPort( 0xA1 , 0xFF ) ;
其中 0xFD 就是 11111101 ;即只允许 PIC1的第二个中断请求,即键盘中断请求。

完工!本篇任务已经胜利完成~~~ ^_^,所有源代码可在如下地址下载
ftp://202.118.239.46/Incoming/Other/BTC/temp/pyos/pyos3.zip
BTW:
在本实验进行的过程中,在BBS上得到了很多老师同学的鼓励,正是由于这种支持力量的
存在,使我获得了将本实验进行下去的力量,在此深表感谢!同时对于此中不计其数的错
误,非常希望各位老师、同学、大牛小牛:P~~,批评指教!
仍然留个mail:
xieyubo@126.com

woodheadmail 2004-01-02
  • 打赏
  • 举报
回复
很喜欢,值得看呀!
xiaohan13916830 2004-01-01
  • 打赏
  • 举报
回复
to abitz:
我还没去网络中心申请呢,没有 :(
elliking 2004-01-01
  • 打赏
  • 举报
回复
值得看呀
加载更多回复(23)

69,371

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧