一个操作系统的实现

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

4 特权级

我首先假定内核的level=0 系统服务的level=1 应用程序的level=3。程序从一个代码段转移到另一个代码段时(可能的情况是调用系统函数),需要考虑权限问题,利用了CPL,DP L和RPL来判断是否可以进行代码转移。

利用call调用门实现一个低特权级的进程访问高特权级的代码段,利用ret指令实现高特权级到低特权级的跳转。

Jmp时,不管长跳转还是短跳转都一样的实现。Call时,长跳转需要比短跳转额外保存一下当前进程的cs地址到堆栈段中(因为返回时需要知道要返回到哪个代码段)。例如用户进程A(ring3)执行时通过调用门访问系统进程B(ring0)的代码段(可以理解为一个系统函数调用),执行完后返回,这个过程可以归纳如下:

1)用户进程A将参数,返回值,cs地址保存到LDT进程A的堆栈段中。

2)用户进程A的程序通过call调用门跳转到系统进程B中执行。由于现在已经是B中的代码在执行,所以相应的使用的是B的堆栈段。同时,A的堆栈段的位置ss和esp会保存到TSS (每个进程一个)

3) B中相应的代码执行完毕(对应命令ret),执行完毕后通过取出TSS中保存的A的堆栈段中的返回地址来返回A。

反过来,由一个系统进程(ring0)进入用户进程(ring3)的关键在于,在ring0向ring3跳转之前,手动保存ring3进程A的当前cs,eip,ss,esp到A的ss中,然后调用命令retf,这个命令会自动加载A的cs,eip,ss,esp到CPU寄存器,并跳转开始执行A。这就实现了ring0 tori ng3。

5 中断和异常

中断是一个计算机执行的根本,进程调度,键盘输入等等都是中断。保护模式下,中断是通过IDT(中断描述符表)来实现的,中断发生时,会在IDT中找到对应的描述符,并转到相应的中断处理函数处运行。终端分为软件中断(通过int N命令实现)和外部硬件中断(例如

|--pm.inc //保护模式相关

|--fat12hdr.inc //文件系统相关

|--include

|--const.h

|--type.h //数据类型

|--protect.h

|--kernel

|--kernel.asm //kernel.bin 其中包含内核入口点_start和异常处理入口点。

|--start.c //内核的C入口cstart() / 初始化IDT

|--lib

|--string.asm //string

注意,在写makefile的时候,至少要3个标签,分别为boot loader kernel 和clean

9这时,内核已经掌握了控制权,但是由于没有中断,内核几乎不能做什么工作(不能进程调度,不能接受输入),所以接下来需要完成中断处理。步骤为:

1)设置和CPU直连的2个8259A芯片的寄存器,将硬件初始化(代码位于starts. c中)。

2)手动建立IDT。

3)建立异常处理,过程如下。

…………//kernel.asm

divide_error: //如果发生异常便会跳到此函数处运行

push 参数(包括错误码和错误的ID)

call exception_hander //除零错处处理函数,显示错误吗

…………..//start.c中运行

idt[INT_VECTOR_DEVIDE].selsect= GDT_idtseclet; //寻址时先在GD T中找到IDT的段地址在加上偏移找到devide_error()的线性地址

idt[INT_VECTOR_DEVIDE].offset= devide_erro r; //错误处理函数为devide_error(); //idt中每一个元素代表了一个中断向量,发生中断时候,CPU会首先根据中断寄存器找到idt的起始位置,然后根据中断向量号和idt数组找到中断处理程序的位置,并保存相关寄存器后跳转到中断处理程序处运行!

4)建立中断处理(步骤和建立异常类似,就是中间多了一个设置初始化8259A)。文章标题:一个操作系统的实现(2)

原作者:xiongjian

原出处:xiongjian Blog

发布者:loose_went

发布类型:转载

发布日期:2010-12-22

今日/总浏览:3/64

10 下面开始进程,首先引入几个基本原则:

1)进程表A用来保存进程A的信息和进程A切换时,保存当前A运行时寄存器信息。定义为:PROCESSproc_table[1024]; 表示系统最多可以有1024个进程,PROCE SS里面就保存了本进程运行时的各个寄存器信息(这样可以在进程切换回来的时候返回寄存器信息),该进程对应的LDT描述符(一般指向代码段和数据段),最后还有一个指向GDT 中该进程位置的描述符。

实现一个最简单的进程切换系统,需要下面3个子模块:

时钟中断处理程序/进程调度模块/至少1个主进程和2个子进程

一个进程(ring1)的最小需要的元素为进程执行体,堆栈大小和位置。另外需要GDT中有一个选择符指向一个该进程的LDT。在时钟中断发生时,时钟中断处理函数(ring0)将esp指向

将要运行的进程在进程表中的位置。

当做到这里,系统应该可以定时触发时间中断处理函数,时间中断处理函数会轮流的调用进程A和B了。(这也是个ring0到ring1直接相互切换的过程)

到此为止,操作系统的启动可以高于段落。

其他需要注意的是:系统调用

系统调用的简单实现就是进程(ring1)将参数例如:num,保存在寄存器中,然后自己产生一个中断,例如int 100h.表示软件发生100H号中断(假设中断处理函数为sys_call)。

I/O system(注意以下都是微内核形态)

IO首先第一个想到的就是键盘,其实键盘驱动非常简单,只要注意以下几个方面即可:

1 建立键盘中断处理函数

2 打开8259a的键盘中断

3 解析中断发生时的数据以确定用户按下的具体按键

4 键盘有一个缓冲区,用于保存用户按键信息,驱动需要及时的吧这个缓冲区的数据读取出来,如果不是一个完整数据,需要做临时缓存。

系统有一个固定的显存内存位置,显示的最基本原理就是N个字节里面保存字符和颜色信息,最常见的是的565色。Linux的多终端机制的一般规则为多个终端对应一个屏幕,用户在使用一个屏幕的时候,可以随时切换到其他屏幕而好像是切换了用户一样。实现这样机制的一个进程叫做TTY进程,他作为第一个微内核以为的系统进程而存在,已区别于一般的用户进程。下面就来看看他的大概实现思路:

Tty_task()

相关文档
最新文档