2024年3月19日发(作者:)
第5期
2011年1O月
微处理机
No.5
MICROPR0CESSORS
Oct.,2011
・
微机软件・
基于X86体系p ̄COS—II在VC6.0下的移植
段玉波,吴光敏
(昆明理工大学理学院,昆明650093)
摘 要: COS—II的范例程序是在Borland C/C++上编译的,对于大多数学习者来说并不熟
悉这个编译环境,为学习 cOs—II还要去适应BC的编译环境。基于Vc++6.0良好的集成编译
环境,探讨了基于x86计算机将 COS—II移植到VC++6.0编译环境,以供教学与学习参考使用。
关键词:txCOS—II操作系统;移植;VC++6.0编译环境
DOI编码:10.3969/j.issn.1002—2279.2011.05.012
中图分类号:TP368.2 文献标识码:B 文章编号:1002—2279(2011)05—0041—05
Porting of uCOS—Il in VC6.0 based on x86
DUAN Yu—b0,WU Guang—rain
(Faculty ofScience,Kunming Univeniq fScoience and Technology,Kunming 650093,China)
Abstract:The original txCOS—II examples are compiled on Borland C/C++,which is not familiar
for most learners,SO they have to adapt to the compile environment before learning COS—II.Based on
VC++6.0 good integrated compile environment.this paper discussed the process of porting the IxCOS—
II to the VC++6.0 compile environment based on x86 computer.aiming to provide teaching and learning
re ̄rence use.
Key words:txCOS—II;Porting;VC++6.0
1移植需要解决的主要问题
移植主要解决三个问题,时钟获得、中断处理和
任务切换。这里仅分析这几个问题的解决思路,后
面第三部分将详细讲述其实现过程。
在PC中,时钟节拍由硬件定时器产生,硬件定
时器会周期性中断CPU。PC机时钟节拍的中断向
量为0x08,IxCOS—II将此向量截取,使之指向
c0s—II的时钟节拍中断服务子程序OSTickISR
(),而原先的中断向量保存在中断向量0x81中。
然而,在基于控制台的windows保护模式下不能像
数调用定时回调函数是和主线程同时被windows操
作系统调度的,并没有起到中断的作用。所以在调
用定时回调函数的时候必须停止主线程的运行,退
出回调函数则恢复主线程的运行。这些事情可以都
放在定时回调函数,也就是 ̄C/OS—II的时钟中断
处理函数中完成。Windows下要挂起一个线程的运
行,首先要得到这个线程的句柄,然后调用Suspend—
Thread(hangdler)和ResumeThread(handler)就可以
挂起和继续执行线程。
任务的切换。任务切换就是进行任务的上下文
切换,研究选择了不带浮点运算的上下文切换进行
DOS下面那么容易,直接通过一个函数调用就能够
修改中断。windows下要修改中断涉及到驱动程
序,这样就加大了移植的困难度与复杂度。因此,考
虑到本移植只是为了教学和学习,并没有应用到对
实时性要求高的产品,所以最终决定采用windows
分析,任务的上下文和 C0s—II在80x86上移植的
上下文很相近,不同点是段寄存器不用保存,因为
VC下任务的切换是在同一个线程完成的,而保护模
式下段寄存器的值在同一个线程下是不改变的。
下的软件定时器来模拟时钟中断。
时钟中断处理子程序。通过软件定时器来模拟
产生I ̄C/OS—II的时钟中断,但timeSetEvent()函
2 Borland C/C++与VC++6.0下编译的主
要区别
p ̄COS—II的作者使用了Borland C/C++编译
作耋简 :收稿日期
:
段玉波(1987一),男,云南省曲靖市人,在读硕士研究生,主研方向:嵌入式系统。
2011—01—13
・
42・ 微处理机
器,如今为便于学习要将其移植到VC++6.0编译
环境下,首先要说明二者的一些区别,因为这直接关
系到与CPU相关的那部分代码的编写。
采用Borland C/C++编译器编译连接源文件
模式下,而VC++6.0建立win32工程文件编译连
接后, cOs—II是在Windows系统平台上作为控制
台应用程序运行于保护模式下,二者主要区别如表
1所示。
后, c0s—II作为DOS程序运行于大存储模式实
表1 DOS程序与控制台程序比较表
Borland C/C++的库文件与VC使用的库文件
有很大差异,Borland C/C++的一些宏也是VC++
32位宽
opt=opt;
6.0所不支持的,因此需要做相应的替换或屏蔽部
分未使用代码。
Borland C/C++编译器对C文件不支持汇编语
言嵌套,而VC++6.0支持在C程序中嵌套汇编代
码。基于此,工程中没有单独的汇编文件,而是将相
关代码放到OS—CPU—C.C中。
stk=(INT32U )ptos;/ 装载空堆栈人口
指针 /
stk=(INT32U)pdata;/ 带参数的函
数调用 /
一一
一一
stk=(INT32U)0x00000000;//子程序
是从当前esp+4处取得传人的参数,所以此处要空
出4个字节
一一
3具体移植步骤
3.1建立和配置工程
stk=(INT32U)task;/ Put pointer to
stk=(INT32U)0x00000202;/ EFL
task on top of stack /
一一
创建工程:建立一个空的win32控制台应用程
序,在此工程内创建相关目录并将 cOs—II文件
添加到其中。
配置工程:将 COs—II相关路径添加到工程配
置选项中;添加与软件定时器有关的库winmm.1ib。
3.2 Includes.h
=0X00000202 /
一一
stk=(INT32U)0xAAAAAAAA;/:}=
/ EAX=0xAAAAAAAA
一一
stk=(INT32U)0xCCCCCCCC;/
stk:(INT32U)0xDDDDDDDD;/
/
ECX=OxCCCCCCCC /
一一
在原有基础上增加两个头文件:
#include<windows.h>
EDX=0xDDDDDDDD
一一
#include<mmsystem.h>//包含时钟函数的头
文件,需要windows.h的支持。
3.3 OS
CPU
—
stk=(INT32U)0xBBBBBBBB;/:l:
EBX=0xBBBBBBBB /
一一
—
C.C文件的修改
=
stk=(INT32U)0x00000000;/ ESP
stk=(INT32U)0xl11111I1;/ EBP
/
stk
=
0x00000000 esp可以任意 /
一一
3.3.1 OSTaskStkInit()
初始化任务堆栈。保护模式下程序是在同一个
段址内处理的,因此段址不用压栈,为研究方便,也
没有把浮点寄存器压栈。Windows保护模式下堆栈
以32位字为单位进行处理,因此数据类型由
=
Oxl111
.一
(INT32U)0x22222222;/ ESI
=
0x2222 /
¥一一stk
=
(INT32U)0x33333333;/:lc EDI
INT16U改为INT32U,代码如下:
程序清单L1初始化任务堆栈
OS
—
=
0x3333 /
retum((OS—STK )stk);/ 返回新的堆栈
STK OSTaskStkInit(void(:lc task)(void
栈顶指针 /
}
3.3.2 OSStartHighRdy()
从处于就绪态优先级最高的任务的TCB中取
pd),void pdata,OS—STK ptos,INT16U opt)
{
INT32U stk;//x86体系console下寄存器为
5期 段玉波等:基于X86体系tzCOS—II在VC6.0下的移植 ・43・
得堆栈地址并恢复到SP,恢复所有寄存器使优先级
一
asm{
最高的任务开始运行,该函数由OSStart()函数调 lea eax,nextstart;任务切换回来后从nextstart
用。代码如下:
程序清单L2启动最高优先级任务
void OSStartHighRdy(void)
{
0STaskSwHook();
OSRunning=TRUE;
一
asm{
nov ebx,[OSTCBCur];0STCBCur结构的第一
个参数就是esp,不用管ess;注:此处OSTCBCur和
OSTCBHighRdy指向同一个TCB
nov esp,[ebx];恢复堆栈
popad;恢复所有通用寄存器,共8个
popfd;恢复标志寄存器
ret;ret指令相当于pop eip但保护模式下不允
许使用eip
;永远都不返回
}
}
3.3.3 OSCtxSw()
任务级的任务切换函数,其实是完成任务的上
下文切换。由于VC++6.0下任务是在一个线程中
切换的,而且保护模式下段址寄存器在同一个线程
下的值不变,因此不用保存段寄存器。任务切换时
的压栈情况如图1所示。
模拟pushad
模拟pushfd——一
子程序是从当前的
保存任务切换地址———_+
esp+4处取得的传
入参数,所以要空
出4个字节
图1 任务切换压栈后状态
程序清单L3手动任务切换
void OSCtxSw(void)
{
开始
push eax
pushfd;标志寄存器的值
pushad;ift存EAX一一EDI
mov ebx,『OSTCBCur]
mov[ebx],esp;把堆栈人口的地址保存到当
前TCB结构中
}
OSTaskSwHook();
OSTCBCur=OSTCBHighRdy;
OSPrioCur=OSPrioHighRdy;
asm{
nov ebx,[OSTCBCur]
mov esp,[ebx];得到OSTCBHighRdy的esp
popad;恢复所有通用寄存器,共8个
popfd;恢复标志寄存器
ret;跳转到指定任务运行
}
nextstart://任务切换回来的运行地址
return;
}
3.3.4 0SIntCtxSw()
中断级任务切换函数,由于控制台程序不能处
理中断,所以此处用库函数模拟中断来实现时钟节
拍终端服务子程序,但其中并没有保存相应寄存器,
因此需要在这里保存CPU寄存器,这与IxCOS—II
原来的程序是不同的。在Test.C中定义了一个主
线程句柄HANDLE和一个CONTEXT保存主线程上
下文。在进行任务切换时,首先保存相应寄存器,然
后把要运行的的任务的上下文填人CONTEXT结构
并保存,完成切换。代码如下:
程序清单L4中断级任务切换函数
extem C0NTExT Context:
extern HANDLE mainhandle;
void OSIntCtxSw(void)
{
OS
—
STK sp;
OSTaskSwHook();
sp=(OS—STK )Context.Esp;//得到主线
程当前堆栈指针
//在堆栈中保存相应寄存器。
一一
sp=Context.Eip;//先保存eip
・
44・ 微处理机
一一
sp=Context.EFlags;//保存en
一一
印 Context.Eax;
:Ic一一sp Context.Ecx:
一一
sp Context.Edx;
一一
sp Context.Ebx:
一一
sp:Context.Esp;//此时保存的esp是
错误的,但OSTCBCur保存了正确的
一一
sp Context.Ebp:
一一
sp Context.Esi:
:l=一一sp=Context.Edi;
OSTCBCur一>OSTCBStkPtr:(OS_STK )sp;
//保存当前esp
OSTCBCur=OSTCBHighRdy;//得到当前就
绪最高优先级任务的tcb
OSPrioCur:OSPrioHighRdy;//得到当前就绪
任务最高优先级数
sp=OSTCBHighRdy一>OSTCBStkPtr;//得到
重新执行的任务的堆栈指针
//恢复所有处理器的寄存器
Context.Edi=:}:sp++;
Context.Esi= sp++:
Context.Ebp sp++:
Context.Esp: sp++;//此时上下文中得到
的esp是不正确的
Context.Ebx’= sp++;
Context.Edx= sp++:
Context.Eex= sp++;
Context.Eax=:}=sp++:
Context.EFlags= sp++;
Context.Eip sp++: ’
Context.Esp=(unsigned long)sp;//得到正确
的esp
SetThreadContext(mainhandle,&Context);//保
存主线程上下文
}
3.3.5 OSTickISR()
在调用timeSetEvent后,被定时器线程被周期
调用,其代码如程序清单I5所示。
程序清单L5时钟节拍ISR
Void CALLBACK OSTickISR(unsigned int a,un-
signed int b,unsigned long e,unsigned long d,un—
signed long e)
{
if(!FlagEn)//通过一个全局变量表示是否屏
蔽中断
return;//如果当前中断被屏蔽则返回
SuspendThread(mainhandle);//中止主线程的
运行,模拟中断产生.但没有保存寄存器
GetThreadContext(mainhandle。&Context);//得
到主线程上下文,为切换任务做准备
OSIntNesting++:
if(OSIntNesting==1){
OSTCBCur一>OSTCBStkPtr=(OS—STK )
Context.Esp;//保存当前esp
}
OSTimeTick();//ucos内部定时
OSIntExit();//由于不能使用中断返回指令,
所以此函数是要返回的
ResumeThread(mainhandle);//模拟中断返回,
主线程得以继续执行
3.4 OS
—
CPU
—
C.H文件的修改
开关中断通过设置一个全局变量来解决,代码
如下:
#define OS
—
ENTER
—
CRITICAL()FlagEn=0//
禁止定时器调度
#define OS
—
EXIT
_
CRITICAL()FlagEn=1//允
许定时器调度
因此在中断处理程序中首先判断当前是否允许
中断,如果允许才挂起主线程,模拟中断产生,完成
进一步的工作。另外,任务切换函数通过宏定义来
调用。
#define OSJ SK—SW()OSCtxSw()//保护模
式下不能中断
由于控制台程序中CPU状态寄存器是32位
的,以及堆栈是以32位字进行处理,因此要做相应
修改:
typedef unsigned int OS
—
CPU
—
SR;
typedef unsigned int OS
—
STK;
3.5 Test.e文件的修改
在main函数的OSInit()前,加入了一个VCInit
()函数,主要初始化VC环境,包括获得主线程的句
柄,设置上下文环境标志位,特别要注意的是,句柄
的获得是要通过伪句柄转换的,代码如下所示:
程序清单L6初始化VC环境
HANDLE mainhandle;//增加的全局变量:主线
程句柄
CONTEXT Context;//增加的全局变量:主线程
5期 段玉波等:基于X86体系 C0S—II在VC6.0下的移植 ・45・
切换上下文
void VCInit(void)
{
HANDLE cp,ct;
//分辨率越高,缺省值为1ms
LPTIMECALLBACK fptc,//指向一个回调函数
DWORD dwUser,//存放用户提供的回调数据
UINT fuEvent);//指定定时器事件类型:
_
Context.ContextFlags=CONTEXTCONTROL;
TIME
—
ONESHOT:uDelay毫秒后只产生一次事件;
PERIODIC:每隔uDelay毫秒周期性地产生
cp=GetCurrentProcess();//得到当前进程句柄
ct=GetCurrentThread();//得到当前线程伪句
柄
TIME
—
事件,注意:该函数包含在mmsystem.h头文件中,同
时需要windows.h的支持,并在工程设置“连接列‘象
Dupli eHandle(cp,ct,cp,&mainhandle,0,
/库模块”中添加winmm 1ib。
TRUE,2);//伪句柄转换,得到线程真句柄
3.8任务堆栈分配
}
尽量为任务分配足够大的堆栈空间,如果分配
3.6随机数的产生
不够,则可能导致调试或运行时发生内存溢出错误。
random函数不是ANSI C标准,不能在gcc,VC
等编译器下编译通过,可改用C++下的rand函数
4结束语
来实现,它由C++标准函数库提供。
通过以上分析进行试验,移植工作成功完成,运
具体实现方法是:首先用srand()来初始化随
行正常,且经过单步跟踪调试也没有出现错误。
机种子数,rand产生的随机数是从0到rand,max
虽然 c0s—II在各种处理器上的移植项目不
的,而rand—max是一个很大的数,因此产生从x到
胜枚举,但单纯在VC++6.0下进行该例程的移植
Y的数:从x到Y有Y—x+1个数,所以要产生从
还未见到。移植的过程可以使人进一步了解与
x到Y的数,只需要这样写:k=rand()%(Y—x+
CPU相关部分程序的编写以及弄懂t ̄COS—II时钟
1)+X。
调用的方法,为深入研究 C0s—II及运用到实际
3.7启动时钟线程
项目中打下基础。
多任务启动后,优先级最高的任务TaskStart()
最后,希望移植后的例程能够为IxCOS—II学
首先运行,该任务调用timeSetEvent()产生定时器
习者提供一个新的学习环境,也为 cOs—II课堂
线程,定时器线程就会按预定的时间周期调用p.C/ 教学提供参考。
Os—II的时钟节拍中断服务子程序OSTickISR()。
参考文献:
代码如下:
[1] Jean J Labr0sse.嵌入式实时操作系统 ̄c/os—II[M].
timeSetEvent(1000/OS_TICKS—.PER—.SEC,0,
北京:北京航空航天大学出版社,2003.
OSTickISR,0,TIME
—
PERIODIC);//开启一个定时
[2] 沈美明,温东掸.IBM—PC汇编语言程序设计[M].北
器线程
京:清华大学出版社,1999.
timeSetEvent函数原型说明:
[3] 罗清平.基于X86体系结构的 ̄COSII移植研究[D].
MMRESULT timeSetEvent(UINT uDelay,//以毫
成都:四川大学,2008.
[4]Jean Louis Gareau.Porting ̄C/OS—II to the x86 Protec.
秒指定事件的周期
ted Mode[EB/OL].2001.http://theblog.tistory.corn/
UINT uResolution,//以毫秒指定延时精度,数
71
值越小定时器事件
特约声明
本刊已人编《中国学术期刊网》(光盘版)、中国科学技术信息研究所《万方数据网》、科学技术部西南信
息中心《中文科技期刊数据库》和(台湾)华艺数位艺术股份有限公司《中文电子期刊服务》
。
其作者文章著
作权使用费与本刊稿费一次性付清。凡不同意人编的稿件,请作者在投稿时声明
。


发布评论