基于X86 体系μCOS-II 在VC6.0 下的移植

2011-07-20 09:40段玉波吴光敏
微处理机 2011年5期
关键词:保护模式寄存器线程

段玉波,吴光敏

(昆明理工大学理学院,昆明 650093)

1 移植需要解决的主要问题

移植主要解决三个问题,时钟获得、中断处理和任务切换。这里仅分析这几个问题的解决思路,后面第三部分将详细讲述其实现过程。

在PC 中,时钟节拍由硬件定时器产生,硬件定时器会周期性中断CPU。PC 机时钟节拍的中断向量为0x08,μCOS- II 将此向量截取,使之指向μCOS-II的时钟节拍中断服务子程序OSTickISR(),而原先的中断向量保存在中断向量0x81 中。然而,在基于控制台的windows 保护模式下不能像DOS 下面那么容易,直接通过一个函数调用就能够修改中断。windows 下要修改中断涉及到驱动程序,这样就加大了移植的困难度与复杂度。因此,考虑到本移植只是为了教学和学习,并没有应用到对实时性要求高的产品,所以最终决定采用windows下的软件定时器来模拟时钟中断。

时钟中断处理子程序。通过软件定时器来模拟产生μC/OS-II的时钟中断,但timeSetEvent()函数调用定时回调函数是和主线程同时被windows 操作系统调度的,并没有起到中断的作用。所以在调用定时回调函数的时候必须停止主线程的运行,退出回调函数则恢复主线程的运行。这些事情可以都放在定时回调函数,也就是μC/OS-II的时钟中断处理函数中完成。Windows 下要挂起一个线程的运行,首先要得到这个线程的句柄,然后调用Suspend-Thread(hangdler)和ResumeThread(handler)就可以挂起和继续执行线程。

任务的切换。任务切换就是进行任务的上下文切换,研究选择了不带浮点运算的上下文切换进行分析,任务的上下文和μCOS-II 在80x86 上移植的上下文很相近,不同点是段寄存器不用保存,因为VC 下任务的切换是在同一个线程完成的,而保护模式下段寄存器的值在同一个线程下是不改变的。

2 Borland C/C++与VC++6.0 下编译的主要区别

μCOS-II的作者使用了Borland C/C++编译器,如今为便于学习要将其移植到VC ++6.0 编译环境下,首先要说明二者的一些区别,因为这直接关系到与CPU相关的那部分代码的编写。

采用Borland C/C ++ 编译器编译连接源文件后,μCOS-II 作为DOS 程序运行于大存储模式实模式下,而VC ++6.0 建立win32 工程文件编译连接后,μCOS-II是在Windows 系统平台上作为控制台应用程序运行于保护模式下,二者主要区别如表1 所示。

表1 DOS 程序与控制台程序比较表

Borland C/C++的库文件与VC 使用的库文件有很大差异,Borland C/C ++的一些宏也是VC ++6.0 所不支持的,因此需要做相应的替换或屏蔽部分未使用代码。

Borland C/C++编译器对c 文件不支持汇编语言嵌套,而VC ++6.0 支持在c 程序中嵌套汇编代码。基于此,工程中没有单独的汇编文件,而是将相关代码放到OS_CPU_C.C 中。

3 具体移植步骤

3.1 建立和配置工程

创建工程:建立一个空的win32 控制台应用程序,在此工程内创建相关目录并将μCOS-II 文件添加到其中。

配置工程:将μCOS-II相关路径添加到工程配置选项中;添加与软件定时器有关的库winmm.lib。

3.2 Includes.h

在原有基础上增加两个头文件:

#include <windows.h >

#include <mmsystem.h >//包含时钟函数的头文件,需要windows.h的支持。

3.3 OS_CPU_C.C 文件的修改

3.3.1 OSTaskStkInit()

初始化任务堆栈。保护模式下程序是在同一个段址内处理的,因此段址不用压栈,为研究方便,也没有把浮点寄存器压栈。Windows 保护模式下堆栈以32 位字为单位进行处理,因此数据类型由INT16U 改为INT32U,代码如下:

程序清单L1 初始化任务堆栈

3.3.2 OSStartHighRdy()

从处于就绪态优先级最高的任务的TCB 中取得堆栈地址并恢复到SP,恢复所有寄存器使优先级最高的任务开始运行,该函数由OSStart()函数调用。代码如下:

程序清单L2 启动最高优先级任务

3.3.3 OSCtxSw()

任务级的任务切换函数,其实是完成任务的上下文切换。由于VC++6.0 下任务是在一个线程中切换的,而且保护模式下段址寄存器在同一个线程下的值不变,因此不用保存段寄存器。任务切换时的压栈情况如图1 所示。

图1 任务切换压栈后状态

程序清单L3 手动任务切换

3.3.4 OSIntCtxSw()

中断级任务切换函数,由于控制台程序不能处理中断,所以此处用库函数模拟中断来实现时钟节拍终端服务子程序,但其中并没有保存相应寄存器,因此需要在这里保存CPU 寄存器,这与μCOS-II原来的程序是不同的。在Test.c 中定义了一个主线程句柄HANDLE 和一个CONTEXT 保存主线程上下文。在进行任务切换时,首先保存相应寄存器,然后把要运行的的任务的上下文填入CONTEXT 结构并保存,完成切换。代码如下:

程序清单L4 中断级任务切换函数

3.3.5 OSTickISR()

在调用timeSetEvent 后,被定时器线程被周期调用,其代码如程序清单L5 所示。

3.4 OS_CPU_C.H 文件的修改

开关中断通过设置一个全局变量来解决,代码如下:

3.5 Test.c 文件的修改

在main 函数的OSInit()前,加入了一个VCInit()函数,主要初始化VC 环境,包括获得主线程的句柄,设置上下文环境标志位,特别要注意的是,句柄的获得是要通过伪句柄转换的,代码如下所示:

3.6 随机数的产生

random 函数不是ANSI C 标准,不能在gcc,vc等编译器下编译通过,可改用C ++下的rand 函数来实现,它由C+ +标准函数库提供。

具体实现方法是:首先用srand()来初始化随机种子数,rand 产生的随机数是从0 到rand_max的,而rand_max是一个很大的数,因此产生从X 到Y的数:从X 到Y 有Y-X +1个数,所以要产生从X 到Y的数,只需要这样写:k=rand()%(Y-X +1)+X。

3.7 启动时钟线程

多任务启动后,优先级最高的任务TaskStart()首先运行,该任务调用timeSetEvent()产生定时器线程,定时器线程就会按预定的时间周期调用μC/OS-II的时钟节拍中断服务子程序OSTickISR()。代码如下:

3.8 任务堆栈分配

尽量为任务分配足够大的堆栈空间,如果分配不够,则可能导致调试或运行时发生内存溢出错误。

4 结束语

通过以上分析进行试验,移植工作成功完成,运行正常,且经过单步跟踪调试也没有出现错误。

虽然μCOS-II 在各种处理器上的移植项目不胜枚举,但单纯在VC ++6.0 下进行该例程的移植还未见到。移植的过程可以使人进一步了解与CPU相关部分程序的编写以及弄懂μCOS-II时钟调用的方法,为深入研究μCOS-II 及运用到实际项目中打下基础。

最后,希望移植后的例程能够为μCOS- II 学习者提供一个新的学习环境,也为μCOS-II 课堂教学提供参考。

[1]Jean J Labrosse.嵌入式实时操作系统μC/OS-II[M].北京:北京航空航天大学出版社,2003.

[2]沈美明,温东掸.IBM-PC 汇编语言程序设计[M].北京:清华大学出版社,1999.

[3]罗清平.基于X86 体系结构的μCOSII 移植研究[D].成都:四川大学,2008.

[4]Jean Louis Gareau.Porting μC/OS-II to the x86 Protected Mode[EB/OL].2001.http://theblog.tistory.com/71.

猜你喜欢
保护模式寄存器线程
STM32和51单片机寄存器映射原理异同分析
基于C#线程实验探究
猫科动物的保护色
Lite寄存器模型的设计与实现
基于国产化环境的线程池模型研究与实现
移位寄存器及算术运算应用
论网络游戏著作权的保护模式及其侵权判定标准
浅谈linux多线程协作
CBD体系下传统知识保护模式研究
宁夏盐池县小麦野生近缘植物蒙古冰草保护模式探究