如何实现一个最简单的操作系统
发布时间:2008/5/27 0:00:00 访问次数:1480
这里为了简单,就不考虑可移植性开求,不从boot部分来接收参数,也不对硬件进行检测,也不需要进行data段,代码段的重定位。我只是读了linux内核相关部分,并未自己去实现一个操作系统,所以我以下所说的只是概念性的东西:
1.接管系统的中断处理,由于boot部分的代码决定了那个中断向量表,从而决定了系统中断之后进入的内存位置,但boot并不知道操作系统的中断处理函数位置所在啊,怎么办呢?有几种方法,其一是:如果你的板子可以重映射地址,也就是可以将内存条所在的位置重映射成0x0开始,那么在链接内核的时候,就将操作系统自己的中断向量表定位在0x0处并且在bootloader引导结束时就完成映射操作,并让cpu跳转到0x0处执行;如果没有重映射功能,我就不晓得怎么办了,不过我想到一个折衷的办法,就是在bootloader启动完成时(也就是将cpu控制权交给操作系统内核时),重新改写flash的0x0区域,就是将操作系统的内核的中断向量表写入flash区的0x0处,比如,当一个irq发生时,cpu决定了会跳入0x18(假设这里flash占用地址总线0x0至0x0fffffff,内存占用0x20000000至0x2fffffff),而bootloader在最后将0x18处的代码修改成了0x20000000加上0x18的地址处的代码,而这个地址就是内核的中断向量表中的相关跳转指令,就相当于跳转进了内核所关联的irq处理函数的地址上去执行中断处理函数了,而这样的不好之处在于:当系统重新上电之后,boot的中断向量表已经被修改,除非boot本身不使用中断,呵,在这样简单的系统中,boot是不需要中断功能的
2.这里为了简单,所以没有使用分页内存管理,就不需要建立页表等操作,直接进行操作系统的堆栈设置,同boot一样的设置过程一样,接着就进行bss段清零操作,这里的bss段是指操作系统自身的bss段,与boot的bss段是同一个含义只是用在了不同的地方了,接着就跳入了main函数
3.为了最大可能的简单,采用静态建立任务结构数组,比如只建立十个任务,那么首先要为这十个任务结构分配段内存,可以在堆上分配(这个分配的内存直到操作系统结束才会被释放,当然也可以指定一片操作系统的其它地方都用不到的内存区域,不过这样写的话就有点外行的味道了,而符务结构数组的指针却是全局变量,存放在bss段或者data段),
由于在上一步中已经分配了一个系统堆栈,那么我们这十个任务就分享这总体的堆栈区域这里的重点就是如果定义每个任务结构数组里面的结构,可以参照linux的相关部分设计
4.中断处理:在第一步中已经确定了cpu进行相关的几类型的中断跳转地址,而相同类型的中断却只有一个入口地址,这里的中断处理就会完成以几个动作:其一:入栈操作,包括所有寄存器入栈,至于这个栈,就是在第二步中所设置的irq栈,其二:屏掉所有中断,呵,这里为了简单起见,所以在处理中断时不允许再次发生中断其,三:读取中断相关的寄存器,判别是发生了什么中断,以至于跳进相关的中断处理函数中去执行(在这里只包括两种中断,一是时钟中断,另一个是swi中断,也就是所谓的系统调用时需要用到的)其四:等待中断处理完成,然后就开启中断并出栈,恢复现场,将cpu控制权交给被中断的代码处
注意:
其一:在mian中必须首先确定整个系统有哪些需要处理的中断,也就是有哪些中断处理函数,然后才编写这里的中断处理函数
其二:本操作系统不处理虚拟内存,其至连cpu异常都不处理(一切都为了简单),一旦
发生异常,系统就死机
5.对timer的实现,首先确定时间片,为了让系统更稳定,而且我们不需要实时功能,尽可能让时间片设置长一点,比如我们让一个任务运行20个时钟滴答数,然后应根据系统频率来确定每个系统滴答所占用的毫秒,这里使用5毫秒让系统定时器中断一次,那么就需要写时钟寄存器,具体参阅芯片资料,计算下来,一个任务最大可能连续运行100毫秒,注意:我们的操作系统不支持内核抢占,同时只支持两级中断优先级,就是只有时钟中断的优先级高一点,其它的优先级都低一级,但是在中断处理一节中却屏掉了这个功能因为一进入中断处理,就禁止中断,所以不管其它中断优先级有多高都没有用的,这样做优点是简单了,但不好之处显而易见,特别在相关中断处理函数如果进入了死循环,那么整个系统就死了,而且时间片也变得不准确了,反正都不用实时,也不需要实时钟支持嘛至于中断优先级设置请参阅芯片资料
6.进程调度的实现,也就是do_timer函数(时钟中断处理函数),有一个全局变量指针,指向的就是当前任务结构数组(或者链表),当时钟中断时,就进入此函数中,首先判断任务结构体中的时间片是否用完,如未用完,就减一,然后退出中断,让cpu继续运行当前的任结构,若用完了时间片,就重置时间片,并重新寻找任何结构数组中的下一个等待
运行的任务,若找到了,就切换至新的任务,至于如何切换,请见下一页描述,如果未找到就切换到idle任务
这里为了简单,就不考虑可移植性开求,不从boot部分来接收参数,也不对硬件进行检测,也不需要进行data段,代码段的重定位。我只是读了linux内核相关部分,并未自己去实现一个操作系统,所以我以下所说的只是概念性的东西:
1.接管系统的中断处理,由于boot部分的代码决定了那个中断向量表,从而决定了系统中断之后进入的内存位置,但boot并不知道操作系统的中断处理函数位置所在啊,怎么办呢?有几种方法,其一是:如果你的板子可以重映射地址,也就是可以将内存条所在的位置重映射成0x0开始,那么在链接内核的时候,就将操作系统自己的中断向量表定位在0x0处并且在bootloader引导结束时就完成映射操作,并让cpu跳转到0x0处执行;如果没有重映射功能,我就不晓得怎么办了,不过我想到一个折衷的办法,就是在bootloader启动完成时(也就是将cpu控制权交给操作系统内核时),重新改写flash的0x0区域,就是将操作系统的内核的中断向量表写入flash区的0x0处,比如,当一个irq发生时,cpu决定了会跳入0x18(假设这里flash占用地址总线0x0至0x0fffffff,内存占用0x20000000至0x2fffffff),而bootloader在最后将0x18处的代码修改成了0x20000000加上0x18的地址处的代码,而这个地址就是内核的中断向量表中的相关跳转指令,就相当于跳转进了内核所关联的irq处理函数的地址上去执行中断处理函数了,而这样的不好之处在于:当系统重新上电之后,boot的中断向量表已经被修改,除非boot本身不使用中断,呵,在这样简单的系统中,boot是不需要中断功能的
2.这里为了简单,所以没有使用分页内存管理,就不需要建立页表等操作,直接进行操作系统的堆栈设置,同boot一样的设置过程一样,接着就进行bss段清零操作,这里的bss段是指操作系统自身的bss段,与boot的bss段是同一个含义只是用在了不同的地方了,接着就跳入了main函数
3.为了最大可能的简单,采用静态建立任务结构数组,比如只建立十个任务,那么首先要为这十个任务结构分配段内存,可以在堆上分配(这个分配的内存直到操作系统结束才会被释放,当然也可以指定一片操作系统的其它地方都用不到的内存区域,不过这样写的话就有点外行的味道了,而符务结构数组的指针却是全局变量,存放在bss段或者data段),
由于在上一步中已经分配了一个系统堆栈,那么我们这十个任务就分享这总体的堆栈区域这里的重点就是如果定义每个任务结构数组里面的结构,可以参照linux的相关部分设计
4.中断处理:在第一步中已经确定了cpu进行相关的几类型的中断跳转地址,而相同类型的中断却只有一个入口地址,这里的中断处理就会完成以几个动作:其一:入栈操作,包括所有寄存器入栈,至于这个栈,就是在第二步中所设置的irq栈,其二:屏掉所有中断,呵,这里为了简单起见,所以在处理中断时不允许再次发生中断其,三:读取中断相关的寄存器,判别是发生了什么中断,以至于跳进相关的中断处理函数中去执行(在这里只包括两种中断,一是时钟中断,另一个是swi中断,也就是所谓的系统调用时需要用到的)其四:等待中断处理完成,然后就开启中断并出栈,恢复现场,将cpu控制权交给被中断的代码处
注意:
其一:在mian中必须首先确定整个系统有哪些需要处理的中断,也就是有哪些中断处理函数,然后才编写这里的中断处理函数
其二:本操作系统不处理虚拟内存,其至连cpu异常都不处理(一切都为了简单),一旦
发生异常,系统就死机
5.对timer的实现,首先确定时间片,为了让系统更稳定,而且我们不需要实时功能,尽可能让时间片设置长一点,比如我们让一个任务运行20个时钟滴答数,然后应根据系统频率来确定每个系统滴答所占用的毫秒,这里使用5毫秒让系统定时器中断一次,那么就需要写时钟寄存器,具体参阅芯片资料,计算下来,一个任务最大可能连续运行100毫秒,注意:我们的操作系统不支持内核抢占,同时只支持两级中断优先级,就是只有时钟中断的优先级高一点,其它的优先级都低一级,但是在中断处理一节中却屏掉了这个功能因为一进入中断处理,就禁止中断,所以不管其它中断优先级有多高都没有用的,这样做优点是简单了,但不好之处显而易见,特别在相关中断处理函数如果进入了死循环,那么整个系统就死了,而且时间片也变得不准确了,反正都不用实时,也不需要实时钟支持嘛至于中断优先级设置请参阅芯片资料
6.进程调度的实现,也就是do_timer函数(时钟中断处理函数),有一个全局变量指针,指向的就是当前任务结构数组(或者链表),当时钟中断时,就进入此函数中,首先判断任务结构体中的时间片是否用完,如未用完,就减一,然后退出中断,让cpu继续运行当前的任结构,若用完了时间片,就重置时间片,并重新寻找任何结构数组中的下一个等待
运行的任务,若找到了,就切换至新的任务,至于如何切换,请见下一页描述,如果未找到就切换到idle任务
上一篇:如何实现BOOTLOADER
上一篇:嵌入式测试中数据获取的几种方式