自制操作系统代码(操作系统编程)

http://www.itjxue.com  2023-02-03 11:44  来源:未知  点击次数: 

如何自己制作一个操作系统

你是一个真正的强人,也许我可以帮你。首先,你需要一个引导扇区(用汇编写),接下来你需要一个与引导扇区相对应的引导器(比如ntldr、bootmgr、grldr、ieldr,当然这里要你自己写,可以用汇编或C)。现在你的程序是这样的:开机通电,BIOS读取硬盘引导扇区数据,引导扇区加载引导器。接下来你要编写你的操作系统内核(用汇编或C)并让你的引导器加载你的系统内核或内核的一个加载模块。在编写操作系统内核时,你要考虑清楚如下问题:内存如何管理?是多进程还是单进程?处理器是在实模式还是在保护模式(保护模式要求自己编写外设驱动)?然后你要编写你的系统所需的文件系统。总之,问题多多。

更多内容,建议你读《Orange's一个操作系统的实现》。

如何从零开始写一个简单的操作系统

如何从零开始写一个简单的操作系统?

看了这个:从零开始写一个简单的操作系统 求指教。

知乎用户 学生生涯只剩半年。

548 人赞同

终于可以来回答这道题了……

一年多前,也就是大一下学期末的时候,我看到这个问题下 @fleuria 叔的答案,然后看了 F 叔给的这个链接 基于 Bochs 的操作系统内核实现 ,当然是什么都看不懂,除了惊诧之外也了解了一件事情:一个人写一个简单的操作系统内核是一件非常帅气并且可行的事情。

于是我开始写了,那时候我的水平大概是:只会做 C 语言的习题,编译的话只知道按 F9,汇编知道常见的指令,另外会一点点的 Win 32 编程,能流畅使用 Windows。

一开始我找了《30 天自制操作系统》来看,每天看书,然后把从书里把代码打出来,一次一次地编译运行。因为要同时写汇编和 C,所以从那时候起就开始用 vim。

在啃完了差不多半本书后,开始觉得没意思了……因为觉得作者为了让内容更简单而省略了太多细节。也看了于渊的《Orange‘s 一个操作系统的诞生》,依然没看下去:汇编用得太多了。期间也曾斗胆发邮件给 F叔,然后他推荐了 Bran's Kernel Development Tutorial 这个教程,于是我就从这教程重新开始了: 「30天自制操作系统」 Stop 「OS67 」 Start

那时候大概是大二上学期,于是在 github 上又开了一个 repo,一开始在 Windows 下开发,后来又切换到了 Linux 下,因为 Bran's 用的 bootloader 是 Grub,不符合我的初衷,所以就自己写了一个,之后便跟一路教程写,跨过了保护模式这道坎,完成了基本的设备驱动。

在完成 Bran's 后,我又部分参考了 写一个操作系统内核有多难?大概的内容、步骤是什么? - To浅墨的回答 中推荐的:hurley25/hurlex-doc · GitHub 文档,完成了一些简单的调试函数和库函数,printk 和内存分配。

事实证明,尽早写好调试函数诸如 panic, assert 和 printk 是非常重要的。 大量地使用有助于你尽快地发现 bug (当然前提是这些函数本身不能有 bug)。

看完了 hurlex-doc 该看的部分后,很长一段时间了都不知道该干嘛好,模仿 hurlex-doc 里的内核线程切换也一直出错。这一情况一直持续到我开始读 Xv6, a simple Unix-like teaching operating system 。

如果你去看知乎关于「自制内核」的问题,你会发现 xv6 被反复地提及并推荐,事实上它非常值得被推荐:这是我读完大部分代码之后真切体会到的。

之前的 Bran‘s 和 hurlex-doc 的篇幅都比较小,我是在电脑和 kindle 上看完的,xv6 相对来说代码量比较大,有 9000+ 行和一份文档,之后我又找到了这个:ranxian/xv6-chinese · GitHub xv6 文档的中文译版,所以我就去花了十二块钱学校打印店打印了一份中文文档和一份代码。这又是一个正确的决定,让我不必对着电脑就能看代码。

在之后的时间里,我先读了 xv6 中文件系统相关的部分,然后改写它的代码为我的内核添加了一个 类似 Minix 的文件系统。 然后几乎又照抄了其中了进程调度的部分(做了部分简化),又在原来的代码基础上为添加操作系统的接口,接着写用户程序,过程几乎是「一路顺风」。看 xv6 的那段时间也经常是处于醍醐灌顶的状态。

最后我终于在差不多一个月前完成了这个简陋的操作系统内核:

LastAvenger/OS67 · GitHub (没错其实我是来骗 star 的)

历时一年,一路点亮了不少技能树(虽然都点得不好),这样算是「从零开始写一个简单的操作系统」么? 跟进一步说,有谁不是从零开始的呢?所以想做的话,现在就开始做好了。

这是被「翻烂」了的 xv6 源代码和中文文档(其实是放书包里被磨烂了)

「故事」讲完了,接下来说一点经验之谈吧……

* 知乎上总是有人在讨论「做一个玩具编译器和做一个玩具内核何者更有趣」之类的问题,然后总有各种大V 跳出来说内核有多 dirty 而编译器多 clean,事实上除了 CPU 上的几个表因为历史原因长得恶心一点,内核并没有什么特别 dirty 的地方,另外,想做点什么打发时间,不过是两个代码量稍多的入门项目,有什么好纠结的?

* 写内核的过程中,你会接触到一些这辈子大概只会用到一次的知识,A20 线已经成为历史,日常的编程里面也不需要你懂得 GDT IDT 的结构。但是单凭内核主要部分部分(文件系统,进程,内存)给你带来的知识而言,这点冗余是值得的。

* 尽早实现调试函数并大量使用,善于利用 bochs 的内置调试器,能省下你不少时间。

* 有时候觉得书里的做法非常奇怪,你觉得你有更好的做法,一般是你想错了。(当然只是一般)

* 上面说看 xv6 一路顺风是假的,20% 时间在抄代码,80% 的时间用来调试。

* 对我这种能力一般的人来说,「写内核」只是好听的说法,正确的说法是「抄内核」。当然,就算是抄一个,也算是受益匪浅了。

* 抄 xv6 的好处在于,即使你的代码出错了,你可以坚信,正确的答案肯定在 xv6 的代码里,或许只是你还没理解透而已,只要不断地看和理解,你就离正确的道路越来越近。

最后,感谢 @fleuria 在微博和邮件里的多次帮助, @To浅墨 的 hurlex-doc 文档,鲜染同学翻译的 xv6 中文文档, @郭家华 完美地解答了我一开始的疑问,让我在内核中得以使用 C 语言。

在 #archlinuxcn 频道里也得到了很多人的帮助。

发布于 2015-11-09

邱永臣 喜剧演员,兼开发工程师

890 人赞同

大二的时候,老师(中山大学万海)对我们说:“如果有谁能自己写一个内核出来,那么,他平时可以不来听课,也不用做平时作业,做出来还能加分,怎么样,有没有人有兴趣?”

和老师一番讨价还价之后,我成为全年级几百号人里唯一一个自己写内核/整个学期都不去教室听课/任何作业都不做的那个人(代表着我的身边将没有可以提供参考的人,任何资料都只能自己找)。

一开始买了《30天自制操作系统》,上面写着需要软盘还有其它的模拟器,我的初衷是写一个可以烧在真机上一按开机键就能跑起来的那种,所以看了几页后就丢开了。后来又找了国人写的一本,也不是特别符合,也丢开了。

这时我看到了那本教材(俗称绿宝书),约莫800页。之后的两个星期里,我每天泡图书馆,以每小时10页的速度读完了它,在上面乱涂乱画了许多标记。800页的英文书,我从中学到了大量的基本概念(线程进程,内存算法,寻址方式等等)。

接着我寻思直接从网络上而不是从书上寻找资料,TA师兄给我提供了一个OS Development,我照着上边的例子,写了数以千记的汇编代码,习得了汇编技能。

此时,我具备基本的概念知识,对程序的语言也已经理解,知道了虚拟机的调试方法,差的,就只有对内核整体是如何协作不太明白。于是我去找来老师用于教学的PintOS,找来MIT那个项目的代码,还有国内一个高校自制的OS(是几个研究生一起写的),仔细研究了一遍,最后开始写代码。

在那个学期里,我放弃了LOL,一心看代码,写内核,写各种模块,将过程记录在博客上,花了三个月的时间,最终写出一个具备terminal的内核(文件系统没写好,时间不够),可以跑命令,运行函数,管理内存和进程,处理中断。

--------------------------

这个Tittle非常好,衷心希望不要因为莫须有的理由封杀。

如何把自制操作系统写进光盘从而启动电脑

你可以在虚拟机中直接写在硬盘MBR中(虚拟机中安全一些)

你也可以在C:\boot.ini 文件中添加以下信息以形成双系统

C:\MySystem.bin=我的系统 (mysystem.bin是你的.bin程序名字)

不行的话可以下载grub4dos用它来引导你自己编译的系统

在grldr的配置文件menu.lst中加入

root (hd0,0)

chainloader mysystem.bin

写系统咋写

如何动手制作这样的引导扇区呢?这个过程十分简单,

(1)首先按照要求写一个合法的引导程序(通常用汇编,机器码也可以,呵呵);

(2)然后将其通过汇编程序,如NASM汇编成二进制文件;

(3)最后,将这个二进制文件写入到目标盘的第一个扇区。

第一步:写代码

; 文件名:boot.asm

; 代码如下,注意,汇编中通常用“;”来表示注释内容

; 此段代码参考《自己动手写操作系统》(于渊)

;

; 初始化函数

org 07c00h ; 告诉编译器将此段程序加载

; 到内存0x0000:07C00处

mov ax, cs

mov ds, ax

mov es, ax

call PrintStr ; 调用屏幕打印函数

jmp $ ; 无限循环

PrintStr: ; 屏幕打印函数

mov ax, HelloWorld ; 将字符串拷贝到ax

mov bp, ax ; es:bp = 串地址

mov cx, 24 ; cx = 串长度

mov ax, 01301h ; ah = 13, al = 01h

mov bx, 000ch ; 页号为0(bh = 0) 黑底红字(bl = 0ch,高亮)

mov dl, 0

int 10h ; 10h号中断

ret

HelloWorld: db "Welcome to Lee's OS *_*" ; 字符串负值

times 510-($-$$) db 0 ; 用0x0填充剩余的空间使生成

; 的二进制代码刚好为512字节

dw 0xaa55 ; 结束标志

; 整个程序结束!很短吧

第二步:汇编

假设你已经安装了NASM程序,那么进入命令行模式,然后输入以下命令:

C:[PATH]\ nasm boot.asm -o boot.bin

其中“C:[PATH]\”为boot.asm代码文件所在位置。

现在如果不出意外的话,你已经拥有了boot.bin二进制文件,这个就是引导程序!

第三步:制作引导盘

由于不能随便更改硬盘,否则系统无法进入原来的操作系统,所以我们用软盘来试验。

那么,我们准备一张软盘。

现在,我们要自己写一个程序将我们汇编得到的二进制文件写到软盘的第一个扇区。

C语言程序代码如下:

/***************START***************/

#include

#include

int main(void)

{

FILE *in;

unsigned char buffer[520];

if((in = fopen("boot.bin", "rb"))==NULL)

{

printf("Error loading file\n");

exit(0);

}

fread(buffer, 512, 1, in);

while(biosdisk(3, 0, 0, 0, 1, 1, buffer));

fclose(in);

return 0;

}

/****************END****************/

注意,这个程序必须同boot.bin文件在同一目录下,然后将软盘放进软驱,运行此程序。

第四步:GOGOGO

好了,现在你拥有了一张从头到尾完全自制的引导盘,由于有了她,你想运行你的电脑再也不需要微软插手了,而且这很可能是你第一次能在裸机上运行一个自己的程序哦,哈哈!

重新启动你的机器,记得把软盘放进去,现在你看见什么了?

一行醒目的红字:

Welcome to Lee's OS *_*

打印在屏幕上!

怎么编写电脑系统?

1. 建立开发环境

这一步非常的简单。

将masm613和vc15的压缩包分别解压到e:masm615和e:msvc15目录下。你也可以放到其他目录下,根据自己的情况而定,但是下面用到的编译命令需要作相应的修改。也不需要添加或修改任何的环境变量。

2. IBM PC的启动及当时的内存使用情况

这一部分内容已经是老生常谈了,但又不能不说。我们只说从硬盘引导的情况。

当BIOS经过POST(Power On Test Self)后,将硬盘MBR读到内存0x0000:0x7C00的位置,然后从这里开始执行。一般的情况,MBR将选择活动分区进行操作系统的启动。在MBR开始执行时,内存使用的情况如下图所示,地址数据用16进制表示:

这已经是老掉牙的内容了,但是,在20年前却十分流行。如果想更详细的了解这方面的内容,找本讲解DOS的书看看吧。

我们自己的操作系统将被加载到0x1000:0x0100。这不是必需或者必然的,是人为选择的,你也可以将其放在0x4321:1234等其他地方。但是,上图中注明有其他用途的内存区域,应该保留,否则,你会后悔的。

3. 开发操作系统

我们自己的操作系统运行在实模式环境下(如果您不知道什么是实模式,也请看看20年前出版的当时非常流行的书,或者直接请教当时的前辈高手)。即使你的电脑是P4的CPU,刚启动时,也只相当于主频较高的8086而已。但是,没有关系。

首先,使用汇编语言写一个框架,文件名是entry.asm:

;

; entry.asm

; Copyright (C) 2004, Tian XiangYuan

;

.MODEL TINY,C

.386p

option expr32

option casemap:none

cmain PROTO NEAR C

.CODE

ORG 0100h ;偏移地址

_start:

jmp begin

nop

DB 'TianXiangYuan',0 ;the magic of my os

begin:

cli

mov ax,cs

mov ds,ax

mov es,ax

mov ss,ax

mov sp,0FFFFh

sti

call cmain ;调用C语言写的主函数

mov ax,4c00h ;调用DOS的功能(为了调试),与我们自己的操作系统无关

int 21h

这段代码非常简单,应该没有什么问题。

已经说了,操作系统将从0x1000:0x0100加载,说是无心,实则有意。我们知道,TINY模式的程序,在DOS下运行时,其起始地址就是0x0100,前面的256Byte是参数部分。如果直接将操作系统在系统启动时加载到0x1000:0x0100,调试时非常麻烦。我们将其起始地址设为0x0100,使其可以在DOS下运行(这也是在程序的最后包含int 21h指令的原因),确认正确无误后,再进行下一步的开发。

下面再看C语言的代码,文件名是main.c:

……

static void InitShell()

{

}

void cmain()

{

InitShell();

TermShell();

}

顾名思义,其中实现了一个简单的shell。因为该程序本身是操作系统的一部分,所以,平时经常使用的一些C库函数,在这里就不能使用了。总之,一切都要自己动手实现。幸好,在实模式下,几乎所有的设备的驱动都包含在BIOS中了,我们可以直接使用。否则,连从键盘读一个键值这样的事都需要自己写键盘的驱动程序,实在太难了。也是这个原因,我们自己的操作系统没有将CPU转到保护模式下,有心之人可以试试。

下面的事情几乎都可以使用C语言实现了。

第一,初始化显示模式。系统启动时,显卡已经被初始化成3模式了,就是80X25的彩色模式(除非你的显示器是单色显示器),我们不需要再做什么了。当然,你也可以将显卡设成VGA甚至SVGA模式,只要你的BIOS和显卡支持。

第二,实现一个具有简单交互功能的shell。代码不全,请自己补齐,或参看附件。

/*

*从键盘读一个字符,如果没有输入,则等待;返回值的低字节为asii码,高字节为键盘扫描码

*/

static int getch()

{

int chr=0;

__asm

{

mov ah,00h

int 16h

mov chr,ax

}

return chr;

}

/*

*使用TTY模式向屏幕输出一个字符

*/

static void putch(unsigned char key)

{

__asm

{

mov bh,0

mov al,key

mov ah,0Eh

int 10h

}

}

#define KEY_BACKSPACE 0x08

#define KEY_ENTER 0x0D

#define KEY_NEWLINE 0x0A

#define KEY_ESCAPE 0x1B

static int printk(const char* str,...)

{

…… //给大家一点空间,自己实现吧

}

static void endline()

{

putch(KEY_NEWLINE); //Line Feed (LF)

putch(KEY_ENTER); //Enter (CR)

}

static char msg_prompt[]="CMD:";

static void deal_cmd(char* cmd_line,int cmd_len)

{

…… //也请大家自己实现吧,例如,可以实现help,dir,cls,halt等命令

…… //其实,就是字符串比较的过程

}

static void TermShell()

{

char cmd_line[80]={0,};

int cmd_len=0;

endline();

printk(msg_prompt,sizeof(msg_prompt));

for (;;)

{

cmd_line[cmd_len]=getch();

switch(cmd_line[cmd_len])

{

case KEY_ENTER:

if (cmd_len1)

deal_cmd(cmd_line,cmd_len);

//break;

case KEY_ESCAPE:

cmd_len=0;

endline();

printk(msg_prompt,sizeof(msg_prompt));

break;

case KEY_BACKSPACE:

if (cmd_len0)

{

putch(0x08);

putch(' ');

putch(0x08);

cmd_len--;

}

break;

default:

putch(cmd_line[cmd_len]);

cmd_len++;

}

}

}

更复杂、功能更强大的方法请参考BIOS的相关文档。也请大家发挥想象力,不断的扩展功能。说心里话,这个“操作系统”比dos还原始!但毕竟是自己的操作系统。

C语言如何自制操作系统?

用objcopy做成二进制,格式是:objcopy -I elf格式 -S -R ".eh_frame" -R ".comment" -O binary elf文件 最终的二进制文件.例如: objcopy -I elf32-i386 -S -R ".eh_frame" -R ".comment" -O binary a.obj a.bin

(责任编辑:IT教学网)

更多