提高系统移植性_第1页
提高系统移植性_第2页
提高系统移植性_第3页
提高系统移植性_第4页
提高系统移植性_第5页
已阅读5页,还剩8页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

嵌入式系统程序可移植性设计及性能优化之三--函数设计嵌入式系统程序可移植性设计及性能优化之三 函数设计【摘要】本章介绍了函数设计的一些基本规则。合理对各种参数进行封装,不但有利于模块的交互,更能够减少参数提高函数调用性能。其次介绍了模块划分的原则,如何减少模块间的耦合度。接着分析了宏函数中参数的基本规则、多宏语句的基本规则、宏和内联的区别以及如何防止宏参数宏语句扩展后的异常效应。最后介绍了如何利用const来进行输入参数的修饰及如何提高程序的可靠性。【关键词】嵌入式,可移植性,函数设计,模块划分,耦合度,内聚性,内联,宏参数,const,输入参数,可靠性3函数设计...-15-避免过多函数参数,提高调用性能…-15-合理设计模块,减小耦合度...-16-用宏函数提高时间效率...-18-宏参数的基本规则…-18-宏语句的基本规则…-18-宏的副作用…-20-3.4 Const修饰输入指针参数…-21-1函数设计避免过多函数参数,提高调用性能在函数设计时通常需要传递参数,为了提供函数调用的性能,某些处理器如ARM会利用寄存器来传递参数,这样不需要访问内存,其访问效率更高。但寄存器传递的参数数目有限,对于ARM体系是四个,当多于四个时,剩余的参数需要用栈传递,参数的出栈入栈都需要时间,调用性能下降。当参数之间紧密相连且通常需要在多个模块中联合使用时,应对参数进行封装,这样便于参数的传递和变量的管理。如:SYS_STATUSDCCM_SetSfnTsn(u16u16SfnPeriod,u16u16TsnPeriod,u16u16Sfn,u16u16Tsn,u32u32TodPart1,u32u32TodPart2)函数六个参数,对这些参数的解析在函数内部又要定义六个变量与之对应,这给变量的定义个管理带来了不便,如下:u16 g_u16DCCMSfn;u16 g_u16DCCMTsn;u16 g_u16DCCMSfnPeriod;u16 g_u16DCCMTsnPeriod;STRU_DD_TOD_INFOg_struDCCMTod;因此应将相关联的变量封装为结构体,优势在于:便于管理、定义、声明,避免零散的变量;意义明确,结构清晰;函数调用时避免传递过多参数,提高函数调用性能,参数少不易出错;不足在于对于结构体的访问效率不如单独的变量,但此性能影响很小;为了代码更好的可读性、可移植可维护性性和可靠性,此处结构体的形式更合适。MAT、CCM及MCP都需要用到此类信息,因此应单独提炼出结构体便于各模块的交互;模块间进行通信时要遵循一定的协议,即数据交互时要按照一定的数据格式进行传输,为防止各模块对格式的认识不统一,最好的方式是提供统一的数据结构的定义来进行数据收发BPP端封装为STRU_BPP_TOD_SFN_TSN_PARAMtypedefstructtag_STRU_BPP_TOD_SFN_TSN_PARAM{STRU_DD_TOD_INFOstruTod;u16 u16SfnPeriod;u16 u16TsnPeriod;u16u16Sfn;u16u16u16Tsn;u16}STRU_BPP_TOD_SFN_TSN_PARAM;而MCP端对此结构的定义为typedefstructtag_STRU_DD_MCPBPP_TODTSNSFN_INFO{u32 u32TodPart1;u32 u32TodPart2;u16 u16SfnPrd;u16 u16TsnPrd;u16 u16SfnNum;u16 u16TsnNum;}STRU_DD_MCPBPP_TODTSNSFN_INFO;可以发现,原来BPP和MCP的MAT模块对时隙、子帧、TOD信息的处理并没有采取统一的方式,这样在交互时容易出现问题。并且二者分别定义,有重复劳动。合理设计模块,减小耦合度软件模块设计时需要合理划分模块的层次结构,提高内聚性,降低耦合度,引用全局变量会增强模块之间的耦合度。如:DCCM模块定义了g_u16DCCMTsn、g_u16DCCMSfn等全局变量,调用DBSP模块实现的时隙回调函数DBSP_TslotCb();DBSP模块中DBSP_TslotCb()声明外部变量,通过全局变量引用g_u16DCCMTsn、g_u16DCCMSfn等变量,并且在DBSP_TslotCb()内部只是读取了g_u16DCCMTsn、g_u16DCCMSfn的值。因此更改函数形式,传递参数,而非引用全局变量;不更改参数值,采用值传递voidDBSP_TslotCb(u16u16Sfn,u16u16Tsn)若DBSP_TslotCb不是由定义g_u16DCCMTsn、g_u16DCCMSfn等变量的DCCM模块调用,贝【J必须声明外部变量然后引用,这种耦合是无法避免的。另一个典型例子是资源的申请和释放。在具备操作系统的嵌入式系统中,因为嵌入式系统的内存空间往往是十分有限的,定义过多的全局变量将导致系统内存大量减少,因为全局变量在程序的整个运行期间都占据着同块内存;另外栈空间也是有限的,若在函数内部定义大容量的数据结构时,很可能导致栈溢出。上述两种情况都需要动态内存申请释放来解决,但不经意的内存泄露会很快导致系统的崩溃。所以一定要保证你的malloc和free成对出现,如果你写出这样的一段程序:u8*function(void){u8*p;p=(u8*)malloc(...);if(p==NULL)…;…/*一系列针对p的操作*/returnp;}在某处调用function(),用完function中动态申请的内存后将其free,如下:u8*q=function();free(q);上述代码明显是不合理的,因为违反了malloc和free成对出现的原则,即”谁申请,就由谁释放”原则。不满足这个原则,会导致代码的耦合度增大,因为用户在调用function函数时需要知道其内部细节!正确的做法是在调用处申请内存,并传入function函数,如下:u8*p=malloc(...);if(p==NULL)…;function(p);.free(p);p=NULL;而函数function则接收参数p,如下:voidfunction(u8*p){…/*一系列针对p的操作*/另外一些公用处理模块,为了满足各种不同的调用需要,往往在内部采用了大量的if-then-else结构,这样很不好,判断语句如果太复杂,会消耗大量的时间的,应该尽量减少公用代码块的使用,避免控制耦合。用宏函数提高时间效率在嵌入(inline)操作符变为标准C的一部分之前,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的实时性能,嵌入代码经常是必须的方法。但是使用这种方法在优化程序速度的同时,程序长度变大了,因此需要更多的ROM空间。使用这种优化在宏函数频繁调用并且只包含几行代码的时候,对提高运行效率是最有效的。函数的调用必须要将程序执行的顺序转移到函数所存放在内存中的某个地址,将函数的程序内容执行完后,再返回到转去执行该函数前的地方。这种转移操作要求在转去执行前要保存现场并记忆执行的地址,转回后要恢复现场,并按原来保存地址继续执行。同时函数调用是要使用系统的栈来保存数据的。因此,函数调用要有一定的时间和空间方面的开销,于是将影响其效率。而宏函数不存在这个问题,其只是在预处理的地方将预先写好的代码嵌入到当前程序,不会产生函数调用,所以仅仅是占用了空间,不需要额外时间方面的开销。所以调用一个宏比调用一个函数更有时间效率,在频繁调用同一个宏函数的时候,该现象尤其突出。1.3.1 宏参数的基本规则在引用宏参数时必须括起来,如参数为A,则引用时(A);若是宏表达式,则整个表达式必须括起来,如((A)+3);采用上述规则定义一个"标准”宏MIN,这个宏输入两个参数并返回较小的一个,如下:#defineMIN(A,B)((A)<=(B)?(A):(B))参数和表达式都必须括起来是为了防止在宏参数替换后由于运算符优先级导致的问题,因为宏参数本身可以是表达式,而且当宏在表达式中展开时,你不知道表达式里还有没级别更高的运算1.3.2 宏语句的基本规则对于多个语句一起构成的宏函数,必须保证在宏函数替换后其为一个完整的执行序列,即任何时候都是全部顺序执行完毕。宏语句的最后一句没有语句结束符'';”,而是在引用宏函数时添加。如:#defineMacroA if(conditon);\Sentencel#defineMacroB sentence2;\Sentence3If(condition1)MacroA;ElseMacroB;则宏函数替换扩展后,如下:If(condition1)if(conditon)Sentence1;Elsesentence2;Sentence3;If(conditionl){if(conditon)Sentencel;elsesentence?;}Sentence3;可以看出扩展后的结果和设计情况相差很远,最简单的解决情况是:任何if-else下面的语句都用'、{}”括起来。这样可以保证在宏函数扩展后不会出现异常。If(conditionl){MacroA;}else{MacroB;}If(conditionl){if(conditon)Sentencel;}else{sentence?;Sentence3;}但顾客是上帝,我们不能要求使用我们的宏函数的用户任何时候都用'、{}"来构造if^else语句,我们要本着''严于律己,宽以待人,,的原则来设计我们的宏函数,这样就可以保证无论用户是否操作规范,我们总能达到他们的要求。可有以下两种方式:1) Do....while(0)结构这是宏函数最常见的方式,如:#defineDD_INIT_ISR_TIME_STATS(pstruIsrTime)do\{\(pstruIsrTime)->u32IsrStartTestFlag=0;\(pstruIsrTime)->u32IsrMinGapTime=C_DD_WORD_VAL_MAX;\}while(0)本质上''{}”来保证多条语句为一个执行序列的,那么下列方式可以么?#defineDD_INIT_ISR_TIME_STATS(pstruIsrTime){\(pstruIsrTime)->u32IsrStartTestFlag=0;\(pstruIsrTime)->u32IsrMinGapTime=C_DD_WORD_VAL_MAX;\}看似可以,实则不然,因为宏函数的规则是调用时将添加'';”,那么上述宏函数扩展后即为{(pstruIsrTime)->u32IsrStartTestFlag=0;(pstruIsrTime)->u32IsrMinGapTime=C_DD_WORD_VAL_MAX;};最后一个';”就是多余的,不能编译通过。但单独一行的'、;”是可以的,其表示空语句。这也就是为什么上面添加了一条无用的while(0),起保证宏扩展后是正确的,同时不会对代码产生任何实质性的影响。编译阶段编译器会对上述代码进行优化,while(0)语句实际上是不会执行的,因此上述Do^.while(0)结构是不会损失性能的。2) 逗号表达式'、,”其可以保证多条语句可以作为一个整体执行。虽然是正确的,但扩展后不便于理解,因此使用较少。1.3.3宏函数的副作用利用上述宏函数的基本规则求最小值的代码如下:Temp=MIN(*p++,b);宏函数只是将参数完全替换,即MIN(*p++,b),进行宏展开后为((*p++)<=(b)?(*p++):(b))如果(*p++)<=(b)成立,则表达式的值为(*p++),但由于在(*p++)<=(b)判断过程中改变了p的值,使得此时的?(*p++)非(*?++)<=(b)中的值了,违背了?号表达式的原意。尽管上述代码符合上面我们所说的宏参数及语句使用规则,扩展后仍然会出现问题,对于宏来说唯一的解决方案是避免宏参数为表达式,即改为:x=*p++;Temp=MIN(x,b);这样带入的参数x即为一个确定的值,符合设计要求。通常函数参数为表达式的唯一目的是为了支持链式表达,可以省去一个变量,除此之外并不会提高运行性能。同时对于参数入栈的顺序并没有统一的规定,这样某些编译器的差异会导致表达式参数值与设计的差异。因此应避免使用表达式参数。从上面的阐述,可以看到宏有一些难以避免的问题,怎么解决呢?内联函数是代码被插入到调用者代码处的函数。如同宏函数,内联函数通过避免被调用的开销来提高执行效率,尤其是它能够通过调用被编译器优化。内联函数和宏函数很类似,而本质区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销,因此其执行速度比一般函数要快。你可以象调用函数一样来调用内联函数,而不必担心会产生于宏函数的一些问题。由上面的两者的特性可知,我们可以用内联函数完全取代预处理宏函数。内联inline函数将进行参数检查,求出参数的值后再将此值带入函数中,因此((A)<=(B)?(A):(B))中的A是一致的。声明内联函数看上去和普通函数非常相似:externvoidf(u32i,u8u8c);当你定义一个内联函数时,在函数定义前加上inline关键字,并且将定义放入头文件:inlinevoidf(u32i,u8u8c){//...}内联函数inline声明必须是和函数体的定义放在一起,才有效。inlineu32function(u32i){returni*i;}这样我们才算定义了一个内联函数。当然,内联函数也有一定的局限性。就是函数中的执行代码不能太多了,如果,内联函数的函数体过大,一般的编译器会放弃内联方式,而采用普通的方式调用函数。这样,内联函数就和普通函数执行效率一样了。另外对于不支持inline内联函数的编译器来说,宏函数就是唯一的减少函数调用的途径了。1.4Const修饰输入指针参数在C语言中,大多数人的概念是"const意味着常量”,但实则不然,const仅意味着:”只能读的普通变量”,可以称其为”不能改变的变量”,尽管这个说法似乎很拗口,但却最准确的表达了C语言中const的本质。const是用来修饰变量的,在定义时赋初值,在编译阶段需要的常量仍然只能以#define宏定义!Const只是一个修饰符,不管怎么样a仍然是一个指向u32型数据的指针constu32*a;a是一个指向常整型数的指针,此最常见于函数参数,指针可修改,但其指向的内容不能修改。u32*consta;a是一个指向整型数的常指针,指针指向的整型数是可以修改的,但指针是不可修改的。u32const*aconst;a是一个指向常整型数的常指针,指针指向的整型数是不可修改的,同时指针也是不可修改的。上述定义方式的本质在于:const在谁后面谁就不可修改,const在最前面则将其后移一位即可,二者等效也许你可能会问,即使不用关键字,也还是能很容易写出功能正确的程序,那么我们为什么还要如此看重关键字const呢?理由如下:关键字const的作用是为给读你代码的人传达非常有用的信息。例如,在函数的形参前添加const关键字意味着这个参数在函数体内不会被修改,属于"输入参数”。在有多个形参的

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论