摘要:剖析了μC/OS-II实时操作体系在内存办理上存在的缺乏,提出了改善办法,经过一个详细实例描绘了该办法的完成。
关键词:实时操作体系 内存办理 微处理器 链接器
μC/OS-II是一种开放源码的实时操作体系,具有抢先式、多使命的特色,已被运用到很多的微处理器上。尽管该内核功用较多,但仍是有不甚完善的当地。笔者在剖析运用中发现,内核在使命办理(包含使命调度、使命间的通讯与同步)和中止办理上是比较完善的,具有能够承受的稳定性和可靠性;但在内存办理上显得过于简略,内存分区的树立办法有不合理之处。
1 内存办理缺乏之处的剖析
在剖析许多μC/OS-II的运用实例中发现,使命栈空间和内存分区的创立采用了界说大局数组的办法,即界说一维或二维的大局数组,在创立使命或内存分区时,将数组名作为内存地址指针传递给生成函数。这样完成起来当然简略,可是不行灵敏有用。
编译器会将大局数组作为未初始化的大局变量,放到运用程序映像的数据段。数组巨细是固定的,生成映像后不行能在运用中动态地改动。关于使命栈空间来说,数组界说大了会形成内存糟蹋;界说小于了使命栈溢出,会形成体系溃散。关于内存分区,在不知道体系初始化后给用户留下了多少自在内存空间的状况下,很难界说内存分区所用数组的巨细。总归运用大局数组来分配内存空间是很不合理的。
别的,现在的μC/OS-II只支撑固定巨细的内存分区,简略形成内存糟蹋。μC/OS-II将来应该被改善以支撑可变巨细的内存分区。为了完成这一功用,体系初始化后能清楚地把握自在内存空间的状况是很重要的。
2 解决问题的办法
为了能清楚把握自在内存空间的状况,防止运用大局数组分配内存空间,关键是要知道整个运用程序在编译、链接后代码段和数据段的巨细,在方针板内存中是怎么定位的,以及方针板内存巨细。关于最终一条,体系编程人员当然是清楚的,第一条编译器会给出,而怎么定位是由编程人员依据详细运用环境在体系初始化确认的。因而,体系初始化时,假如能正确组织代码段和数据段的方位,就能清楚地知道用户能够自在运用的内存空间开端地址。用方针板内存最高端地址减去开端地址,便是这一自在空间的巨细。
3 举例描绘该办法的完成
下面以在CirrusLogic公司的EP7211微处理器上运用μC/OS-II为例,描绘该办法的完成进程。假定依据μC/OS-II的运用程序比较简略,以简化问题的论述。
3.1 芯片初始化进程和链接器的功用
EP7211采用了RISC体系结构的微处理器核ARMTDMI,该芯片支撑内存办理单元MMU。体系电复位后,从零地址开端履行由汇编语言编写的初始化代码。零地址寄存着中止向量表,第一个是复位中止,经过该中止向量指向的地址能够跳转到体系初始化部分,
履行微处理器寄存器初始化。假如运用虚拟内存,则发动MMU,然后是为C代码履行而进行的C环境初始化。之后创立中止处理程序运用的栈空间,最终跳转到C程序的进口履行体系C程序。
关于运用程序,ARM软件开发包含供给的ARM链接器会发生只读段(read-only section RO)、读写段(read-write section RW)和零初始化段(zero-initialized section ZI)。每种段能够有多个,对较简略程序一般各有一个。
只读段便是代码段,读写段是现已初始化的大局变量,而零初始化段中寄存未初始化的大局变量。链接器一起供给这三种段的开端地址和完毕地址,并用已界说的符号表明。描绘如下:Image$$RO$$Base表明只读段的开端地址,Image$$RO$$Limit表明只读段完毕后的首地址;Image$$Limit表明读写段完毕后的首地址;Image$$ZI$$Base表明零初始化段的开端地址,Image$$ZI$$Limit表明零初始化段完毕后的首地址。
一般嵌入式运用,程序链接定位后生成bin文件,即肯定地址空间的代码,因而上述符号的值表明物理地址。关于简略程序,可在编译链接时指定RO和RW的根底址,协助链接器核算上述符号的值。关于较杂乱的程序能够由scatter描绘文件来界说RO和RW的基地址。
3.2 详细实例及阐明
所谓C环境初始化,便是运用上述符号初始化RW段和ZI段,以使后边运用大局变量的C程序正常运转。下面是初始化部分的实例:
ENTRY ;运用程序进口,应该坐落内存的零地址。
;中止向量表
B Reset_Handler
B Undefined_Handler
B SWI_Handler
B Prefetch_Handler
B Abort_Handler
NOP ;保存向量
B IRQ_Handler
B FIQ_Handler
;当用户运用除复位中止以外的几个中止时,应将跳转地址换成中止处理程序的进口地址。
Undefined_Handler
B Undefined_Handler
SWI_Handler
B SWI_Handler
Prefetch_Handler
B Prefech_Handler
Abort_Handler
B Abort_Handler
IRQ_Handler
B IRQ_Handler
FIQ_Handler
B FIQ_Handler
;程序初始化部分
Reset_Handler
;初始化微处理器寄存器,以使其正常作业。
……
;发动MMU,进入虚拟内存办理。
……
;初始化C环境。
IMPORT |Image$$RO$$Limit|
IMPORT |Image$$RW$$Base|
IMPORT |Image$$ZI$$Base|
IMPORT |Image$$ZI$$Limit|
LDR r0,=|Image$$RO$$Limit|
LDR r1,=|Image$$RW$$Base|
LDR r3,=|Image$$ZI$$Base|
CMP r0,r1
BEQ %F1
0 CMP r1,r3
LDRCC r2,[r0],#4
STRCC r2,[r1],#4
BCC $B0
1 LDR r1,=|Image$$ZI$$Limit|
MOV r2,#0
2 CMP r3,r1
STRCC r2,[r3],#4
BCC %B2
在RAM中初始化RW段和ZI段后,ZI段完毕后的首地址到体系RAM最高端之间的内存便是用户能够自在运用的空间,也便是说Image$$ZI$$Limit是这一内容空间的开端地址。
假如体系运用了 FIQ和IRQ中止,在ZI段之后能够创立这两种中止的栈空间,然后是操作体系运用的SVC形式下的栈空间,假定每一个栈巨细为1024个字节。假如体系运用了定时器,还可在此之后创立定时器中止的栈空间,假定其巨细也为1024个字节。此刻自在内存空间的开端地址变为:
Image$$ZI$$Limit+1024×4
在初始化代码的最终将其作为一个参数传递到C程序进口,代码如下:
LDR r0,=|Image$$ZI$$Limit|
;创立IRQ栈空间。
……
;添加地址指针。
ADD r0,r0,#1024
;创立FIQ栈空间。
……
;添加地址指针。
ADD r0,r0,#1024
;创立SVC栈空间。
……
;添加地址指针。
ADD r0,r0,#1024
;创立定时器中止栈空间。
……
;添加地址指针。
ADD r0,r0,#1024
;导入C代码进口点。
IMPORT C_ENTRY
;跳转到C代码,此刻r0作为进口参数。
B C_ENTRY
3.3 对实例的总结
在C程序中,上述开端地址能够作为内存分区创立函数OSMemCreate()的内存地址参数,内存分区的最大值便是方针板RAM的最高端地址减去开端地址的值。图1显现了RO段在RAM中的内存散布状况,这种状况下,程序映像一般被保存方针板内存中。体系从闪存发动后,将RO段拷贝到RAM中持续履行。图2显现了RO段在闪存中,RW和ZI段在RAM中的状况。这种状况下,体系发动和代码的履行都发生在闪存中。
用户知道开端地址的值和自在内存的巨细后,就能够清楚、灵敏地树立和运用内存分区了。能够依据详细需求树立一些巨细不同的内存分区,使命栈、事情操控块和音讯行列都能够在这些内存分区中分配。体系能够十分明晰地把握内存运用状况。
本文针对一种芯片描绘了怎么完成对μC/OS-II内存办理的改善。关于其它类型的微处理器,例如CISC指令集的芯片,尽管详细完成进程有所不同,但思路是相同的。
μC/OS-II的内存办理还有需求改善的当地,例如,现在的内存办理只支撑固定巨细的分区,而实践运用中有动态分配非固定分区的需求。这就要求μC/OS-II有完成该功用的软件结构和内存分配、收回算法。现在能清楚地把握体系初始化后内存散布状况,为往后完成这些软件结构和算法打下了根底。