积分规则 网站地图 帮助中心    
嵌入式软件 单 片 机 DSP 存储器 传感控制 光电显示
嵌入式硬件 CPLD/FPGA SOPC AD/DA 接口电路 模拟设计
I C设计 通信产品 汽车电子 电源产品 消费电子 数控系统
工业控制 军工/航天 安防产品 医疗电子 计算机外设 测试测量
供应 I C
求购 公司库

  IC 求购 销售 公司 论文 DATASHEET 参考设计 论坛
当前位置: 电子技术 >> 嵌入式硬件 >> X86
  相关分类: 相关文章 | ARM | 新产品 | 下载 | MIPS | X86 |
一个基于X86的小型中文操作系统的设计、编码与调试 (一)
 
作者:陈斌   来源:本站原创    点击数:721   更新时间:2007-2-6
您可以添加到网摘 让更多人关注此文章:

     简介:本文通过一个基于X86的小型中文操作系统的建立过程,说明了一个小型操作系统的设计原理,编码过程和实现方法。作为一个开放源代码的系统,本文通过对源代码的剖析,较为详细地说明了内存管理,进程结构等的具体实现,并最后介绍了在开发过程中对产生的系统内核的调试,对操作系统的实践具有一定的指导意义。

一 引言

操作系统是计算机的软件基础。在进行一些系统编程实验的时候,我们需要对一些系统程序进行试验,并对这些程序进行量化评测。现有的大型操作系统,如WindowsLinux过于复杂,不适合进行试验。并且由于系统本身的开销比较大,对程序的量化评测要扣除系统(如进程调度)的开销,不易把握。因此我们自行设计开发了一个基于X86的小型中文操作系统。本文介绍了在开发中采用的一些方法和技巧,探讨小型操作系统的实作过程。

二 设计

1.       微内核

微内核是内核的一种形式。在微内核中,各个独立的模块被分离出来,作为独立的实体存在(进程)。在系统运行过程中,各个模块的进程独立运行,各进程通过消息通讯机制进行通讯。由于微内核系统具有良好的结构和可移植性,且易于调试,所以在设计中我们采用了微内核作为我们的内核结构。

2.       内存布局/管理

X86提供的保护模式(Protected Mode)中,可以采用分段和分页两种方法来对内存进行管理[1]。我们可以通过对GDT或是LDT的相应的描述符表项来对内存的相应段进行设置,如段边界、段大小、特权级等属性[2]。在一个小型的系统中,可以通过将系统的整个内存空间全部设为一个段,即一个平的内存段,来达到简化系统的目的。这样做的缺点是不能有效利用到保护模式的一些优点,如段数据的保护。在NASM中,我们通过以下的代码对GDT进行段的设置:

_gdt:

     dw 0, 0, 0, 0          ; (0)

    

     ; kernel cs 0x08 (1)

     dw 0x3FFF    ; base: 0, limit: 64M

     dw 0x0000

     dw 0x9A00

     dw 0x00C0

 

     ; kernel ds 0x10 (2)

     dw 0x3FFF    ; base: 0, limit: 64M

     dw 0x0000

     dw 0x9200

     dw 0x00C0

    

     ; user cs 0x1b (3)

     dw 0x3FFF    ; base: 0, limit: 64M

     dw 0x0000

     dw 0xFA00    

     dw 0x00C0

 

     ; user ds 0x23 (4)

     dw 0x3FFF    ; base: 0, limit: 64M

     dw 0x0000

     dw 0xF200

     dw 0x00C0

3.       进程结构

多任务是一个现代操作系统所必须的一部分。在一个任务切换到另一个任务的过程中,我们必须保存现在正在运行的这个进程的上下文,以便在下次调入运行时能够在现在断下的地方继续运行,然后再调入下一个应该运行的进程的上下文,开始下一个进程的运行。在我们的系统中,简单地保存了下面的这些信息:

struct proc_struct

{

     int           pid; // 进程id

     int           parent;

 

     struct proc_struct* next_proc; // 下一个进程

     struct proc_struct* prev_proc; // 上一个进程

     // 进程状态

     long eip;

     long eax;

     long ebx;

     long ecx;

     long edx;

     long esp;

     long ebp;

     long edi;

     long esi;

     long eflags;

     // 段寄存器

     short       cs;

     short       ds;

     short       es;

     short       ss;

     short       fs;

     short       gs;

     // 进程已经运行时间

     int           total_tick;

     unsigned char stack[STACK_NUM]; // 堆栈段

};

其中pid字段记录了这个进程的IDparent记录了父进程的IDnext_proc是指向下一个要运行的进程的上下文的结构指针,以便系统调入下一个进程运行时进行快速定位,prev_proc记录了在这个进程之前运行的进程结构地址。由此可见,在我们的实现在,我们采用了双向链表对进程进行管理,既简单,又对进程的增加、删除、调序带来了方便。对于当前正在运行的进程,系统用struct proc_struct* p_proc这个指针来指向其进程结构。

在进行了以上的设置以后,我们就可以实现简单的进程管理系统了。process_schedule函数在进程双向链表中循环切换,在一个进程运行完一定的时间后,直接载入下一个进程的上下文,然后跳到下一个进程的执行点,进行执行。

int process_schedule(void)

{

     // 进行一些进进程管理

     if(p_proc->next_proc == NULL)

     {

            // 只有这一个进程运行,跳回去继续运行

            jump_to_proc(p_proc);

     }

     p_proc = p_proc->next_proc; // 转到下一个进程

     jump_to_proc(p_proc); // 切换过去   

     return 1;

}

其中,jump_to_proc这个函数在取得下一个进程的上下文后,将上下文装入CPU各寄存器中,然后运行jmp指令跳到进程的执行点中进行执行。

void jump_to_proc(struct proc_struct* p)

{

     // 跳到指定的进程中去运行

     temp_ebp = p->ebp;

     temp_eip = p->eip;

     temp_eax = p->eax;

     temp_ebx = p->ebx;

     temp_ecx = p->ecx;

     temp_edx = p->edx;

     temp_esp = p->esp;

     temp_edi = p->edi;

     temp_esi = p->esi;

     temp_eflags = p->eflags;

     __asm__("movl %0,%%ebp\n\t"                

                   "movl %2,%%ebx\n\t"

                   "movl %3,%%ecx\n\t"

                   "movl %4,%%edx\n\t"

                   "movl %5,%%esp\n\t"

                   "movl %6,%%edi\n\t"

                   "movl %7,%%esi\n\t"

                   "pushl %8\n\t"

                   "popfl\n\t"                    

                   "movl %1,%%eax\n\t"

                   "sti\n\t" 

                   ::"m"(temp_ebp),                 

                   "m"(temp_eax),

                   "m"(temp_ebx),

                   "m"(temp_ecx),

                   "m"(temp_edx),

                   "m"(temp_esp),

                   "m"(temp_edi),

                   "m"(temp_esi),

                   "m"(temp_eflags));              

                   switching = 0;              

     __asm__("jmp *%0\n\t"

                   ::"m"(temp_eip));

     // 此函数不应被返回

}

4.       文件系统

FAT16FAT32EXT2等文件系统中,FAT32由于其实用性和简单性,我们选用它作为实现在文件系统。在硬盘驱动的基础上,我们简单地实现了FAT32文件系统的读功能,这样在后续的开发中,我们可以将各个功能模块作成单独的文件调入执行,增大系统的灵活性。

5.       启动设计

X86系统加电后BIOS执行完自检,将启动设备的第一个扇区读入到物理地址为0x7C00的内存单元中,然后跳到0x7C00去运行。由此可见,操作系统与BIOS的接口就在于启动设备(软驱,硬盘驱动器或是光盘驱动器)的第一个扇区。一般在这第一个扇区完成操作系统的初步载入。但我们在实践中发现,在一个扇区这么小的空间内(512个字节),将整个系统载入比较困难,且如果安装在PC上会与已经存在的操作系统发生冲突。

在这种情况下,我们想到GNUGRUB启动装载器。GRUB能够支持多个操作系统的共存,通常我们将系统内核编译成GRUB启动装载器能够识别的文件格式,GRUB便能将系统加载到指定的内存地址中去,然后跳到我们的系统中去运行。



相关文章
· 嵌入式x86:非PC设计的要旨?[68]
· Embedded x86: keystone of your non-PC design?[202]
· VxWorks for X86的输入输出定向[177]
· VxWorks for X86的输入输出定向[217]
· ARM、x86对决移动互联,谁将最后胜出?[244]
热门评论排行
·VHDL设计中电路简化问题的
·江苏嵌入式Linux教育培训
·锐极LINUX驱动培训班定于
·基于实时操作系统μC/OS-
·ARM处理器应用开发4步骤

文章评论
    没有任何评论
*只显示最新10条评论。评论内容只代表网友观点,与本站立场无关。更多评论
发表评论
  * 请先[登陆]再进行评论,谢谢。
评分: 1分 2分 3分 4分 5分
内容: *
发帖须知:
一.所发文章必须遵守《互联网电子公告服务管理规定》;
二.严禁发布供求代理信息,公司介绍,产品信息等广告宣传信息;
三.严禁恶意重复发帖;
四.严禁对个人,实体,民族,国家等进行漫骂,污蔑,诽谤。
 
热点新闻 [更多]
 
各种冲值卡批发销售!独家特价!
特价销售GPS车载导航系统!
数字电视批发销售!你想不到的价格!
品牌相机批发销售!你想不到的价格!
品牌手机批发销售!你想不到的价格!
笔记本电脑批发销售!你想不到的价格!
那一年我做了鸭子2
那一年我做了鸭子1
等你十年(下)
等你十年(上)
 
热门下载 [更多]
 
[ ] 手把手学单片机20个例
[ ] 单片机做的智能台灯
[ ] 单片机入门书
[ ] linux系统移植开发文档
[ ] IC卡的读写程序
[ ] 8051单片机C语言彻底应用
[ 常用软件] 555定时器电路设计软件V1.2
[ 常用软件] 51定时器计算软件
[ ] ARM处理器应用开发4步骤
[ ] 实用电源电路集锦
 
论坛新帖 [更多]
 
新店开业,部分开发板&元器件特...
[原创]VB编程RS232串口...
红外摄像机控制故障分析与处理方...
红外防水摄像机在智能视频监控中...
步进电机不用驱动芯片能驱动吗?...
我要选一块双串口,带AD的单片...
[分享]运放和功放的区别是什么...
4×4矩阵式键盘的课程设计...
做过高速信号滤波的高手帮忙...
做视频信号放大器,各位有什么好...

 
赞助商 [更多]
 


ICP许可证号:[粤 05056597]
联系电话:010-82517432 82517615 传真: 010-82517615

版权所有 Copyright © 2006 嵌入式技术网