嵌入式系統(tǒng)程序可移植性設(shè)計(jì)及性能優(yōu)化_第1頁(yè)
嵌入式系統(tǒng)程序可移植性設(shè)計(jì)及性能優(yōu)化_第2頁(yè)
嵌入式系統(tǒng)程序可移植性設(shè)計(jì)及性能優(yōu)化_第3頁(yè)
嵌入式系統(tǒng)程序可移植性設(shè)計(jì)及性能優(yōu)化_第4頁(yè)
嵌入式系統(tǒng)程序可移植性設(shè)計(jì)及性能優(yōu)化_第5頁(yè)
已閱讀5頁(yè),還剩37頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、嵌入式系統(tǒng)程序可移植性設(shè)計(jì)及性能優(yōu)化【摘要】在嵌入式系統(tǒng)的程序設(shè)計(jì)中,由于軟硬件平臺(tái)的多變性,對(duì)程序的可移植性、可擴(kuò)充性、可裁減性及可維護(hù)性等有更嚴(yán)格的要求。本文從宏定義設(shè)計(jì)、數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)及函數(shù)設(shè)計(jì)等方面,簡(jiǎn)單介紹了可移植性的設(shè)計(jì)問(wèn)題。在嵌入式應(yīng)用中非常注重代碼的時(shí)空效率,即產(chǎn)生的代碼運(yùn)行時(shí)間和占用的存儲(chǔ)空間盡可能少。程序設(shè)計(jì)一章介紹了如何提高程序的運(yùn)行效率的相關(guān)技巧?!娟P(guān)鍵詞】嵌入式,可移植性,可維護(hù),可裁減,宏定義設(shè)計(jì),數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì),時(shí)空效率目錄1宏定義設(shè)計(jì)- 1 -1.1為何要采用宏定義?- 1 -1.2宏定義的基本規(guī)則- 1 -1.3依賴關(guān)系定義宏改善移植性- 1 -1.4通過(guò)偏移量和

2、掩碼進(jìn)行位操作- 2 -2數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)- 4 -2.1結(jié)構(gòu)體中成員對(duì)齊規(guī)則- 4 -2.1.1自然對(duì)界- 4 -2.1.2指定對(duì)界- 4 -2.2合理設(shè)計(jì)成員順序- 5 -2.2.1減少結(jié)構(gòu)體存儲(chǔ)空間- 5 -2.2.2填充部分域,避免字節(jié)對(duì)齊問(wèn)題- 6 -2.2.3字節(jié)對(duì)齊問(wèn)題實(shí)例- 7 -2.3采用位域構(gòu)造結(jié)構(gòu)體- 8 -2.3.1位域設(shè)計(jì)傳輸協(xié)議- 8 -2.3.2位域的可移植性問(wèn)題- 9 -2.3.3位域設(shè)計(jì)硬件配置字- 10 -2.4通過(guò)union和struct傳遞不同格式報(bào)文- 11 -2.5將相關(guān)功能變量封裝為結(jié)構(gòu)體- 13 -3函數(shù)設(shè)計(jì)- 15 -3.1避免過(guò)多函數(shù)參數(shù),提高調(diào)

3、用性能- 15 -3.2合理設(shè)計(jì)模塊,減小耦合度- 16 -3.3用宏函數(shù)提高時(shí)間效率- 18 -3.3.1宏參數(shù)的基本規(guī)則- 18 -3.3.2宏語(yǔ)句的基本規(guī)則- 18 -3.3.3宏的副作用- 20 -3.4Const修飾輸入指針參數(shù)- 21 -4程序設(shè)計(jì)- 22 -4.1循環(huán)轉(zhuǎn)置- 22 -4.2減小運(yùn)算強(qiáng)度- 23 -4.2.1位操作實(shí)現(xiàn)求余運(yùn)算- 23 -4.2.2用移位實(shí)現(xiàn)乘除法運(yùn)算- 23 -4.2.3將循環(huán)體內(nèi)的乘法運(yùn)算改成循環(huán)自加運(yùn)算- 23 -4.3減少不變計(jì)算- 24 -4.3.1循環(huán)內(nèi)部避免恒定式- 24 -4.3.2避免結(jié)構(gòu)體深度訪問(wèn)- 25 -4.4減少存儲(chǔ)訪問(wèn)指令

4、周期和個(gè)數(shù)- 26 -4.5查表- 28 -4.6使用自加、自減指令- 28 -4.7根據(jù)頻率進(jìn)行case 排序- 29 -4.8函數(shù)指針表替代switchcase- 30 -嵌入式系統(tǒng)程序可移植性設(shè)計(jì)及性能優(yōu)化之一宏定義設(shè)計(jì)【摘要】本節(jié)介紹了嵌入式系統(tǒng)程序設(shè)計(jì)中采用宏定義進(jìn)行常量定義的必要性。說(shuō)明了宏常量定義的基本規(guī)則以及如何采用依賴關(guān)系定義宏常量來(lái)保證其可移植性和裁減性。最后介紹了如何利用宏定義實(shí)現(xiàn)掩碼偏移量等來(lái)高效的進(jìn)行位操作。【關(guān)鍵詞】嵌入式,可移植性,宏定義,依賴關(guān)系,掩碼,偏移量,位操作1宏定義設(shè)計(jì)- 1 -1.1為何要采用宏定義?- 1 -1.2宏定義的基本規(guī)則- 1 -1.3依

5、賴關(guān)系定義宏改善移植性- 1 -1.4通過(guò)偏移量和掩碼進(jìn)行位操作- 2 -1 宏定義設(shè)計(jì)1.1 為何要采用宏定義?在程序設(shè)計(jì)過(guò)程中,對(duì)于經(jīng)常使用的一些常量,如果將它直接寫到程序中去,一旦常量的數(shù)值發(fā)生變化,就必須逐個(gè)找出程序中所有的常量,并逐一進(jìn)行修改,這樣必然會(huì)降低程序的可維護(hù)性。因此,應(yīng)盡量通過(guò)預(yù)處理命令方式將常量定義為特定的字符,這樣常量就有了統(tǒng)一的表現(xiàn)形式,不會(huì)出現(xiàn)輸入錯(cuò)誤導(dǎo)致的不一致性。另外宏常量意義明確,大大改善了代碼的可讀性。只讀的變量也可以實(shí)現(xiàn)上述宏常量所帶來(lái)的可移植性、可靠性及可讀性等特點(diǎn),但其要占據(jù)存儲(chǔ)空間,需要訪問(wèn)內(nèi)存,相比宏常量的立即數(shù)尋址而言效率要低。在C中提倡用co

6、nst只讀變量來(lái)定義常量,是因?yàn)檫@樣可以提供更嚴(yán)格的類型安全檢查。但由于C中const只讀變量不能用于某些場(chǎng)合,因此在嵌入式C中仍多數(shù)采用宏來(lái)定義常量。1.2 宏定義的基本規(guī)則下面以一個(gè)實(shí)例來(lái)說(shuō)明宏定義的基本規(guī)則,如用預(yù)處理指令#define 聲明一個(gè)常量,用以表明1年中有多少秒,不考慮潤(rùn)年#define C_SECONDS_PER_YEAR (60 * 60 * 24 * 365)ULa) 命名風(fēng)格,為了與普通變量區(qū)分開來(lái),宏定義通常全部大寫,多個(gè)單元之間用下劃線隔開;b) 整個(gè)表達(dá)式應(yīng)括起來(lái),若有參數(shù)則應(yīng)將每個(gè)參數(shù)都括起來(lái),防止替換擴(kuò)展后可能帶來(lái)的異常問(wèn)題;c) 常量表達(dá)式先合并后再替換。

7、預(yù)處理器將為你計(jì)算常量表達(dá)式的值,因此,直接寫出你是如何計(jì)算一年中有多少秒而不是計(jì)算出實(shí)際的值,是更清晰而沒有運(yùn)行性能代價(jià)的。d) 為常量添加類型信息。宏的不足之一在于缺乏類型安全檢查,人為的提供類型信息可以有效檢查出此類問(wèn)題。UL告訴編譯器這個(gè)常量是無(wú)符號(hào)長(zhǎng)整型數(shù),因此將其賦值給u16型變量會(huì)出現(xiàn)告警。 1.3 依賴關(guān)系定義宏改善移植性嵌入式系統(tǒng)程序的最大特點(diǎn)是硬件平臺(tái)的多變性,因此需要根據(jù)具體的應(yīng)用情況更改大量配置,而這些配置基本都是由宏定義來(lái)實(shí)現(xiàn)的,放在特定的頭文件中,與其他的代碼隔離,在一定程度上改善了代碼的可移植性。但有些時(shí)候,多個(gè)宏定義有嚴(yán)重的依賴關(guān)系,增減某個(gè)宏會(huì)引起其他定義的更

8、改,如何定義這些宏對(duì)嵌入式程序的可移植性有很大影響。A常量分別定義#define C_DD_MODULE_ID_AOM (0x) /* AOM模塊ID */#define C_DD_MODULE_ID_RRCM (0x) /* RRCM模塊ID */#define C_DD_MODULE_ID_RLC (0x) /* RLC模塊ID */#define C_DD_MODULE_ID_TRM (0x) /* TRM模塊ID */#define C_DD_MODULE_ID_MCP_MIN (C_DD_MODULE_ID_AOM) #define C_DD_MODULE_ID_MCP_MAX (C

9、_DD_MODULE_ID_TRM)B依賴定義#define C_DD_MODULE_ID_MCP_MIN (0x) /* MCP最小模塊ID */#defineC_DD_MODULE_ID_AOM (C_DD_MODULE_ID_MCP_MIN) /* AOM模塊ID */#define C_DD_MODULE_ID_RRCM (C_DD_MODULE_ID_AOM + 1) /* RRCM模塊ID */#define C_DD_MODULE_ID_RLC (C_DD_MODULE_ID_RRCM + 1) /* RLC模塊ID */#define C_DD_MODULE_ID_TRM (C

10、_DD_MODULE_ID_RLC + 1) /* TRM模塊ID */#define C_DD_MODULE_ID_MCP_MAX (C_DD_MODULE_ID_TRM) /* MCP最大模塊ID */“A常量分別定義”方式,因?yàn)楦鱾€(gè)宏定義值必須連續(xù),若更改或者刪除C_DD_MODULE_ID_AOM,其他的定義基本都受到影響,將嚴(yán)重影響到代碼的可擴(kuò)充性和可裁減性?!癇依賴定義”方式,其原則是:a) base用常量定義;b) 第一個(gè)定義為base;c) 其他的在上一個(gè)基礎(chǔ)上加1; d) max項(xiàng)即為最后一項(xiàng)。這樣整體改動(dòng)起來(lái)只需要更改base;在中間刪除或添加部分項(xiàng)時(shí)只需要更改一個(gè)上下銜接

11、處即可;添加則比較簡(jiǎn)單,只需要在原有最后項(xiàng)后添加即可。這種方式若改動(dòng)部分定義對(duì)其他定義的影響最小,大大改善了代碼的可移植性。1.4 通過(guò)偏移量和掩碼進(jìn)行位操作嵌入式系統(tǒng)經(jīng)常需要和硬件打交道,而配置硬件寄存器則是系統(tǒng)初始化階段的重要任務(wù),如何清晰明了的進(jìn)行配置決定了代碼的可讀性。盡管可以使用位域,但位域是不可移植的,因此利用宏定義來(lái)解決可移植性問(wèn)題。對(duì)于每一個(gè)待操作相應(yīng)位來(lái)說(shuō),應(yīng)具備以下幾個(gè)標(biāo)識(shí):待操作的位名稱B_NAME;操作位所占據(jù)的位寬B_WIDTH_NAME操作位第一位的偏移量B_SHIFT_NAME操作位的掩碼B_MASK_NAME以PRI為例進(jìn)行說(shuō)明:#define PRI DD_C

12、64_EDMA_OPT_PRI#define C_WIDTH _DD_C64_EDMA_OPT_PRI (3)#define C_SHIFT_DD_C64_EDMA_OPT_PRI (29)可以手動(dòng)定義對(duì)應(yīng)位的掩碼,如下:#define C_MASK_DD_C64_EDMA_OPT_PRI (0xe)但更好的方式是利用位寬和偏移量來(lái)自動(dòng)構(gòu)成掩碼#define BIT_MASK(_name) (1U C_WIDTH _#_name)-1)( C_SHIFT_#_name)#define C_MASK_DD_C64_EDMA_OPT_PRI BIT_MASK(PRI)BIT_MASK(PRI)經(jīng)過(guò)

13、宏替換后即為:(1U( C_WIDTH _DD_C64_EDMA_OPT_PRI)-1) /( C_SHIFT_DD_C64_EDMA_OPT_PRI)對(duì)于每個(gè)位的取值也應(yīng)該用宏定義來(lái)表示,這樣清晰明確#define C_DD_C64_EDMA_OPT_URGENT_PRI (0x0)#define C_DD_C64_EDMA_OPT_HIGH_PRI (0x1)#define C_DD_C64_EDMA_OPT_MEDIUM_PRI (0x2)#define C_DD_C64_EDMA_OPT_LOW_PRI (0x3)具備了掩碼和偏移量即可對(duì)各個(gè)位進(jìn)行操作了#define SET_BITS

14、(_reg,_name_val)/(_reg)=(_ reg)&(BIT_MASK(_name) /| (_val)( C_SHIFT_#_ name)&(BIT_MASK(_name)通過(guò)如下方式調(diào)用設(shè)置優(yōu)先級(jí)SET_BITS(u32Opt, PRI, C_DD_C64_EDMA_OPT_HIGH_PRI);即實(shí)現(xiàn)了將u32Opt的PRI等位設(shè)置為C_DD_C64_EDMA_OPT_HIGH_PRISET_BITS宏可應(yīng)用于待操作的位為多位的情況,當(dāng)待操作的位僅為一位時(shí),可用更簡(jiǎn)單的操作方式#define SET_BIT(_reg,_name) (_reg) |= (BIT_MASK(_na

15、me)#define CLR_BIT(_reg,_name) (_reg) &= (BIT_MASK(_name)以TCINT為例:#define TCINT DD_C64_EDMA_OPT_TCINT#define C_WIDTH _DD_C64_EDMA_OPT_TCINT (1)#define C_SHIFT_DD_C64_EDMA_OPT_PRI (20)SET_BIT(u32Opt, TCINT)CLR_BIT(u32Opt, TCINT) 嵌入式系統(tǒng)程序可移植性設(shè)計(jì)及性能優(yōu)化之二數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)【摘要】本章介紹了結(jié)構(gòu)體中成員的對(duì)齊規(guī)則,及在此規(guī)則上如何調(diào)整成員順序或填充部分字段保證其所

16、占內(nèi)存大小不會(huì)因?yàn)榫幾g器的不同導(dǎo)致差異。然后介紹了如何利用位域設(shè)計(jì)網(wǎng)絡(luò)通信協(xié)議及由此帶來(lái)的大小端系統(tǒng)的可移植性問(wèn)題;同時(shí)介紹了用位域在特定平臺(tái)上配置硬件寄存器的技巧。最后介紹了如何利用union在不同系統(tǒng)間傳輸變長(zhǎng)數(shù)據(jù)包及如何進(jìn)行數(shù)據(jù)封裝并提供相關(guān)操作接口的相關(guān)技巧?!娟P(guān)鍵詞】嵌入式,可移植性,數(shù)據(jù)結(jié)構(gòu),結(jié)構(gòu)體對(duì)齊規(guī)則,非對(duì)齊訪問(wèn),位域,傳輸協(xié)議,大小端,硬件配置字,數(shù)據(jù)封裝,初始化操作接口2數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)- 4 -2.1結(jié)構(gòu)體中成員對(duì)齊規(guī)則- 4 -2.1.1自然對(duì)界- 4 -2.1.2指定對(duì)界- 4 -2.2合理設(shè)計(jì)成員順序- 5 -2.2.1減少結(jié)構(gòu)體存儲(chǔ)空間- 5 -2.2.2填充部分域

17、,避免字節(jié)對(duì)齊問(wèn)題- 6 -2.2.3字節(jié)對(duì)齊問(wèn)題實(shí)例- 7 -2.3采用位域構(gòu)造結(jié)構(gòu)體- 8 -2.3.1位域設(shè)計(jì)傳輸協(xié)議- 8 -2.3.2位域的可移植性問(wèn)題- 9 -2.3.3位域設(shè)計(jì)硬件配置字- 10 -2.4通過(guò)union和struct傳遞不同格式報(bào)文- 11 -2.5將相關(guān)功能變量封裝為結(jié)構(gòu)體- 13 -1 數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)程序設(shè)計(jì)是算法和數(shù)據(jù)結(jié)構(gòu)的集合,因此數(shù)據(jù)結(jié)構(gòu)是程序設(shè)計(jì)的基礎(chǔ),就象建造豪華的公寓也必須從一磚一瓦開始。大型的C/C+程序,勢(shì)必要涉及一些進(jìn)行數(shù)據(jù)組合的結(jié)構(gòu)體,這些結(jié)構(gòu)體可以將原本意義屬于一個(gè)整體的數(shù)據(jù)組合在一起。嵌入式系統(tǒng)軟硬件平臺(tái)具備多變性,不同處理器對(duì)于數(shù)據(jù)對(duì)齊

18、訪問(wèn)的要求不同,另外不同的編譯器可以設(shè)置不同的數(shù)據(jù)對(duì)齊規(guī)則,這些都將導(dǎo)致結(jié)構(gòu)體在不同軟硬件平臺(tái)下的可移植性問(wèn)題。在網(wǎng)絡(luò)協(xié)議、通信控制等嵌入式系統(tǒng)的C/C+編程中,經(jīng)常要傳送的不是簡(jiǎn)單的字節(jié)流(u8型數(shù)組),而是多種數(shù)據(jù)組合起來(lái)的一個(gè)整體,其表現(xiàn)形式是一個(gè)結(jié)構(gòu)體。經(jīng)驗(yàn)不足的開發(fā)人員往往將所有需要傳送的內(nèi)容依順序保存在u8型數(shù)組中,通過(guò)指針偏移的方法傳送網(wǎng)絡(luò)報(bào)文等信息。這樣做編程復(fù)雜,易出錯(cuò),按順序存儲(chǔ)的數(shù)組不便于增添其他成分,因此一旦控制方式及通信協(xié)議有所變化,程序就要進(jìn)行非常細(xì)致的修改,移植性差;而結(jié)構(gòu)體的成員增減時(shí)不影響原有單元的操作,因?yàn)榫幾g器會(huì)自動(dòng)計(jì)算各個(gè)成員的偏移量。因此從某種程度上來(lái)

19、說(shuō),會(huì)不會(huì)用struct及怎樣用struct對(duì)程序的可移植性和可讀性有較大影響。1.1 結(jié)構(gòu)體中成員對(duì)齊規(guī)則1.1.1 自然對(duì)界對(duì)于結(jié)構(gòu)體,編譯器會(huì)自動(dòng)進(jìn)行成員變量的對(duì)齊,以提高運(yùn)算效率。缺省情況下,編譯器為結(jié)構(gòu)體的每個(gè)成員按其自然對(duì)界(natural alignment)條件分配空間。各個(gè)成員按照它們被聲明的順序在內(nèi)存中順序存儲(chǔ),第一個(gè)成員的地址和整個(gè)結(jié)構(gòu)的地址相同。自然對(duì)界即默認(rèn)對(duì)齊方式,是指按結(jié)構(gòu)體的成員中size最大的成員對(duì)齊。例如:struct naturalalignu8 u8a;u16 u16b;u8 u8c;在上述結(jié)構(gòu)體中,size最大的是u16,其長(zhǎng)度為2字節(jié),因而結(jié)構(gòu)體中的

20、u8成員a、c都以2為單位對(duì)齊,sizeof(naturalalign)的結(jié)果等于6。1.1.2 指定對(duì)界一般地,可以通過(guò)下面的方法來(lái)改變?nèi)笔〉膶?duì)界條件:a) 使用偽指令#pragma pack (n),編譯器將按照n個(gè)字節(jié)對(duì)齊;b) 使用偽指令#pragma pack (),取消自定義字節(jié)對(duì)齊方式。所有處于“#pragma pack (n)”和“#pragma pack ()”之間的結(jié)構(gòu)體將按照指定對(duì)界對(duì)齊。當(dāng)pragma pack (n)中指定的n大于結(jié)構(gòu)體中最大成員的size,則其不起作用,結(jié)構(gòu)體仍然按照size最大的成員進(jìn)行對(duì)界。例如:#pragma pack (n)struct pa

21、cku8 u8a;u32 u32b;u8 u8c;#pragma pack ()當(dāng)n為4、8、16時(shí),其對(duì)齊方式均一樣,sizeof(naturalalign)的結(jié)果都等于12。而當(dāng)n為2時(shí),其發(fā)揮了作用,使得sizeof(naturalalign)的結(jié)果為8。另外,通過(guò)_attribute(packed (n)也可以單個(gè)結(jié)構(gòu)體的成員對(duì)齊在n字節(jié)邊界,這樣即使平臺(tái)改變了,編譯器不同了,也將采用統(tǒng)一的對(duì)界方式,這種方式對(duì)于不同體系的處理器之間的數(shù)據(jù)交互很重要,移植性好。1.2 合理設(shè)計(jì)成員順序1.2.1 減少結(jié)構(gòu)體存儲(chǔ)空間在默認(rèn)的自然對(duì)界情況下,若最大數(shù)據(jù)類型為u32,則u32四字節(jié)對(duì)齊,u16

22、二字節(jié)對(duì)齊,整個(gè)結(jié)構(gòu)體大小為sizeof(u32)的倍數(shù),該結(jié)構(gòu)體定義的變量首地址自動(dòng)對(duì)齊在sizeof(u32)邊界上。故:struct naturalalignAu16 u16a;u32 u32b;u8 u8c;struct naturalalignBu16 u16a;u8 u8c;u32 u32b;struct naturalalignCu8 u8c;u16 u16a;u32 u32b;sizeof(naturalalignA) = 12sizeof(naturalalignB) = 8sizeof(naturalalignC) = 8struct naturalalignDu8 u8a

23、;u16 u16b;u8 u8c;struct naturalalignEu8 u8a;u8 u8c;u16 u16b;sizeof(naturalalignD) = 6sizeof(naturalalignE) = 4struct naturalalignFu8 u8a;u32 u32b;u8 u8c;struct naturalalignGu8 u8a;u8 u8c;u32 u32b;sizeof(naturalalignF) =12sizeof(naturalalignG) = 8從上面可以看出,從存儲(chǔ)空間的角度看,naturalalignA、naturalalignD、naturala

24、lignF都是不合理的設(shè)計(jì)。1.2.2 填充部分域,避免字節(jié)對(duì)齊問(wèn)題struct naturalalignDu8 u8a;u16 u16b;u8 u8c;struct naturalalignHu8 u8a;u8 padding;u16 u16b;u8 u8c;u8 padding;struct naturalalignIu8 u8a;u8 u8c;u16 u16b;在自然對(duì)界情況下,sizeof(naturalalignD) = sizeof(naturalalignH) = 6此時(shí)并不會(huì)因?yàn)樘畛洳糠钟蚝髮?dǎo)致結(jié)構(gòu)體變大,只是避免編譯器自動(dòng)填充而已。但對(duì)于指定對(duì)界時(shí)可能會(huì)使結(jié)構(gòu)體變大。natu

25、ralalignI調(diào)整了成員的順序,減少存儲(chǔ)空間的同時(shí)保證了u16 u16b的對(duì)齊,無(wú)需設(shè)計(jì)填充域,同時(shí)編譯器也不會(huì)自動(dòng)填充struct naturalalignJu16 u16a;u8 u8c;u8 padding;u32 u32b;struct naturalalignKu8 u8c;u8 padding;u16 u16a;u32 u32b;struct naturalalignLu8 u8c;u16 u16a;u8 padding;u32 u32b;naturalalignJ中填充后保證了u32 u32b的對(duì)齊,naturalalignK填充后保證了u16 u16a的對(duì)齊也保證了u32

26、u32b的對(duì)齊,要注意的是naturalalignL表面上填充域后u32 u32b對(duì)齊了,但由于u16 u16a未處于二字節(jié)對(duì)齊邊界上,實(shí)際上編譯器在u8 u8c后自動(dòng)填充了一個(gè)域保證u16 u16a的對(duì)齊,在u8 padding后自動(dòng)填充了三個(gè)u8保證u32 u32b的對(duì)齊。naturalalignJ和naturalalignK都是合理的填充方式。#pragma pack (2)struct packA u16 u16a;u32 u32b;u8 u8c;#pragma pack ()#pragma pack (2)struct packBu16 u16a;u8 u8c;u32 u32b;#p

27、ragma pack ()#pragma pack (1)struct packCu8 u8a;u16 u16b;u8 u8c;#pragma pack ()在指定對(duì)界情況下,大于pack字節(jié)的將按照設(shè)定值進(jìn)行對(duì)齊sizeof(packA) = 8sizeof(packB) = 8sizeof(packC) = 4由于packA類型變量對(duì)齊在sizeof(u32)邊界上,2字節(jié)對(duì)齊時(shí)u32 u32b的地址沒有對(duì)齊在四字節(jié)邊界上,此時(shí)的影響為:不能進(jìn)行非對(duì)齊訪問(wèn)的處理器:u32 u32temp packA.b將導(dǎo)致處理器內(nèi)存訪問(wèn)異常;可非對(duì)齊訪問(wèn)的處理器:u32 u32temp packA.b對(duì)

28、于b的訪問(wèn)實(shí)際上是分兩個(gè)u16來(lái)分別訪問(wèn)然后合成一個(gè)u32后賦值給u32temp的,比對(duì)齊的u32變量訪問(wèn)效率要低。u32 *u32temp &packA.b,因?yàn)閡32temp為u32型指針變量,其值必須為4的倍數(shù),而(&packA.b)并不是4的倍數(shù),顯然此處的賦值是不合理的。指針類型強(qiáng)制轉(zhuǎn)換時(shí)也可能存在這種異常問(wèn)題。為了避免指定對(duì)界不統(tǒng)一帶來(lái)的內(nèi)存訪問(wèn)異常問(wèn)題,在數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)時(shí)總的原則是:不考慮編譯器自動(dòng)填充的情況下,通過(guò)適當(dāng)填充使u16對(duì)齊在二字節(jié)上,u32對(duì)齊在四字節(jié)上,此時(shí)無(wú)論編譯器何種對(duì)界,結(jié)構(gòu)體的大小總是固定的,且不會(huì)存在內(nèi)存訪問(wèn)問(wèn)題。struct naturalalignEu

29、8 u8a;u8 u8c;u16 u16b;struct naturalalignKu8 u8c;u8 u8padding;u16 u16a;u32 u32b;struct naturalalignHu8 u8a;u8 u8padding;u16 u16b;u8 u8c;u8 u8padding;1.2.3 字節(jié)對(duì)齊問(wèn)題實(shí)例下面以一個(gè)實(shí)例來(lái)說(shuō)明對(duì)齊方式不同導(dǎo)致的不同處理器間的數(shù)據(jù)交互問(wèn)題。三種平臺(tái):a) ARM Linux,嵌入式平臺(tái),未定義數(shù)據(jù)結(jié)構(gòu),轉(zhuǎn)發(fā)字節(jié)流,采用偏移量;b) Windows Mobile PDA,嵌入式平臺(tái),將數(shù)據(jù)組合為了結(jié)構(gòu)體,按照字節(jié)對(duì)齊;c) Windows XP

30、PC,自然對(duì)界。相應(yīng)的數(shù)據(jù)結(jié)構(gòu)定義如下:typedef struct _DBGEstEntry u16 id; u8 hopcount; u8 sendEst; DBGEstEntry;/add by oy 06.8.4typedef struct _DebugPacket u8 estEntries; DBGEstEntry estList; DebugPacket;ARM和PDA交互時(shí)因?yàn)槎际前凑兆止?jié)對(duì)齊的,在將字節(jié)流強(qiáng)制轉(zhuǎn)換為結(jié)構(gòu)體時(shí),對(duì)數(shù)據(jù)的解析方式一致,沒有任何問(wèn)題。后將代碼移植到PC平臺(tái),同樣的程序結(jié)果卻不一樣。究其原因就是因?yàn)椴煌脚_(tái)采用了不一致的對(duì)齊方式。PC平臺(tái)下VC默認(rèn)采用

31、8字節(jié)自然對(duì)界,由于DBGEstEntry最大類型為uu3216_t,因此DebugPacket的成員estEntries后默認(rèn)補(bǔ)齊了一個(gè)u8,相當(dāng)于此時(shí)不同平臺(tái)采用了不一樣的結(jié)構(gòu)來(lái)解析數(shù)據(jù),就出現(xiàn)異常了。添加一個(gè)填充域,指針強(qiáng)制轉(zhuǎn)換時(shí),地址前移一個(gè)以取消填充域的影響。typedef struct _DebugPacket u8 reserved; u8 estEntries; DBGEstEntry estList; DebugPacket;或者針對(duì)此數(shù)據(jù)結(jié)構(gòu)采用指定的對(duì)齊方式,和ARM和PDA平臺(tái)一致。typedef struct _DebugPacket u8 reserved; u8

32、estEntries; DBGEstEntry estList; _attribute(packed (1) DebugPacket;因此對(duì)于嵌入式平臺(tái)上的數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì),一定要合理調(diào)整順序及填充部分域避免平臺(tái)和編譯器的不同導(dǎo)致的字節(jié)對(duì)齊問(wèn)題。1.3 采用位域構(gòu)造結(jié)構(gòu)體1.3.1 位域設(shè)計(jì)傳輸協(xié)議在大多數(shù)情況下,我們一般這樣定義結(jié)構(gòu)體:/* 與MCP驅(qū)動(dòng)通信交互的數(shù)據(jù)幀頭部標(biāo)識(shí)*/typedef struct tag_STRU_DD_FRMHDR u8 u8FrmFlag; /* 幀頭標(biāo)識(shí) */u8 u8SrcModuleId; /* 源模塊ID */u8 u8DstModuleId;/* 目的

33、模塊ID */u8 u8padding;u16 u16Length; /* 數(shù)據(jù)長(zhǎng)度 */u16 u16padding STRU_DD_FRMHDR;對(duì)于一般的應(yīng)用,這已經(jīng)能很充分地實(shí)現(xiàn)數(shù)據(jù)的“封裝”。但是在實(shí)際工程中,由于傳輸鏈路中每次傳輸?shù)臄?shù)據(jù)有限,頭部越長(zhǎng),導(dǎo)致有效數(shù)據(jù)越少,如CAN總線通信中,數(shù)據(jù)總長(zhǎng)8個(gè)字節(jié),有效的減少頭部信息就很重要。由于數(shù)據(jù)長(zhǎng)度u16Length不便縮短,而u8SrcModuleId和u8DstModuleId分別用四位就可表示,因此可以用位域來(lái)表示此結(jié)構(gòu)體。位域就是一個(gè)基本類型變量中的不同的位表示不同的含義。譬如一個(gè)硬件寄存器,假設(shè)為16 bit ,而每個(gè)bit

34、 都可以表達(dá)不同的含義。這個(gè)時(shí)候我們用什么數(shù)據(jù)結(jié)構(gòu)來(lái)表達(dá)這個(gè)寄存器呢?答案還是結(jié)構(gòu)體!這時(shí)要用到結(jié)構(gòu)體的高級(jí)特性,就是在基本成員變量的后面添加“: 數(shù)據(jù)位數(shù)”組成新的結(jié)構(gòu)體,如下:typedef struct tag_STRU_DD_FRMHDRu32 bit8FrmFlag :8; /* 幀頭標(biāo)識(shí) */u32 bit4SrcModuleId:4; /* 源模塊ID */u32 bit4DstModuleId :4;/* 目的模塊ID */u32 bit16Length:16; /* 數(shù)據(jù)長(zhǎng)度 */ STRU_DD_FRMHDR;上述結(jié)構(gòu)體中的四個(gè)成員加起來(lái)只占用了一個(gè)unsigned u32

35、 的空間?;境蓡T變量被拆分后,訪問(wèn)的方法仍然和訪問(wèn)沒有拆分的情況是一樣的。1.3.2 位域的可移植性問(wèn)題在拆分基本成員變量的情況下,我們要特別注意數(shù)據(jù)的存放順序,這還與CPU 是Big endian 還是Little endian 來(lái)決定。Little endian 和Big endian 是CPU 存放數(shù)據(jù)的兩種不同順序。對(duì)于整型、長(zhǎng)整型等數(shù)據(jù)類型,Big endian 認(rèn)為第一個(gè)字節(jié)是最高位字節(jié)(按照從低地址到高地址的順序存放數(shù)據(jù)的高位字節(jié)到低位字節(jié));而Little endian 則相反,它認(rèn)為第一個(gè)字節(jié)是最低位字節(jié)(按照從低地址到高地址的順序存放數(shù)據(jù)的低位字節(jié)到高位字節(jié))。如我們定義

36、IP 包頭結(jié)構(gòu)體為:struct iphdr #if defined(_LITTLE_ENDIAN_BITFIELD) u8 ihl:4, version:4; #elif defined (_BIG_ENDIAN_BITFIELD) u8 version:4, ihl:4; #else #error Please fix #endif u8 tos; u16 tot_len; u16 id; u16 frag_off; u8 ttl; u8 protocol; u16 check; _u32 saddr; _u32 daddr; ; 在Little endian 模式下,iphdr 中定義:

37、u8 ihl:4, version:4; 因?yàn)槲挥蚴菑牡臀蛔止?jié)開始計(jì)算的,其存放方式為:第1 字節(jié)低4 位 ihl ,第1 字節(jié)高4 位 version (IP 的版本號(hào))若在Big endian 模式下還這樣定義,則存放方式為:第1 字節(jié)低4 位 version (IP 的版本號(hào)),第1 字節(jié)高4 位 ihl 這與實(shí)際的IP 協(xié)議是不匹配的,所以在Linux 內(nèi)核源代碼中,IP 包頭結(jié)構(gòu)體的定義利用了宏來(lái)區(qū)分兩種不同的情況。#if defined(_LITTLE_ENDIAN_BITFIELD) #elif defined (_BIG_ENDIAN_BITFIELD) #endif 對(duì)于咱們

38、的應(yīng)用情況,此STRU_DD_FRMHDR是PowerPC和DSP交互時(shí)使用的,目前二者都為big endian模式,因此二者采用位域?qū)?shù)據(jù)解析時(shí)是一致的,但若一方為小端,則數(shù)據(jù)解析將出錯(cuò)。由此我們總結(jié)位域的使用要點(diǎn):a) C/C+ 語(yǔ)言的結(jié)構(gòu)體支持對(duì)其中的基本成員變量按位拆分,其使用方法和結(jié)構(gòu)體一致,便于編程;b) 但要特別注意拆分后的數(shù)據(jù)的存放順序,為了支持代碼的跨平臺(tái)移植,應(yīng)用宏定義來(lái)區(qū)分大小端的不同數(shù)據(jù)結(jié)構(gòu)。1.3.3 位域設(shè)計(jì)硬件配置字在嵌入式系統(tǒng)中經(jīng)常要和底層硬件打交道,此時(shí)需要配置各種硬件選項(xiàng),而這些選項(xiàng)通常都是由一個(gè)寄存器的不同位表示的,進(jìn)行配置時(shí)經(jīng)常要用到與或、移位等操作,編

39、程不便,因此可以利用位域的形式來(lái)訪問(wèn)不同域,其和結(jié)構(gòu)體的訪問(wèn)形式一樣,非常方便。/* struct defined for the OPTION word for EDMA - big endian */typedef struct tag_STRU_DD_C64_EDMA_OPT u32 u32bit3Pri : 3; /* Priority */ u32 u32bit2Esize : 2; /* Element size */ u32 u32bit1TwoDs : 1; /* Source dimension */ u32 u32bit2Sum : 2; /* Source address

40、 update mode */ u32 u32bit1TwoDd : 1; /* Destination dimension */ u32 u32bit2Dum : 2; /* Destination address update mode */ u32 u32bit1Tcu32 : 1; /* Transfer complete u32errupt */ u32 u32bit4Tcc : 4; /* Transfer complete code */ u32 u32bit1Rsvd1 : 1; /* Resverd */ u32 u32bit2Tccm : 2; /* Transfer co

41、mplete code */ u32 u32bit1Atcu32 : 1; /* Alternate transfer complete u32r */ u32 u32bit1Rsvd2 : 1; /* Resverd */ u32 u32bit6Atcc : 6; /* Alternate transfer complete code */ u32 u32bit1Rsvd3 : 1; /* Resverd */ u32 u32bit1Pdts : 1; /* PDT mode for source */ u32 u32bit1Pdtd : 1; /* PDT mode for Destina

42、tion */ u32 u32bit1Link : 1; /* Link */ u32 u32bit1Fs : 1; /* Frame synchronization */ STRU_DD_C64_EDMA_OPT;硬件寄存器通常上電后各位為0,大多數(shù)選項(xiàng)都是默認(rèn)的,只需配置部分非0的選項(xiàng)。采用位域時(shí)只會(huì)更改相關(guān)的配置項(xiàng),不會(huì)影響其他位。而沒有位域時(shí)需要與或等操作避免影響其他位。對(duì)于硬件配置字通常定義相關(guān)常量來(lái)表示不同的配置,如下:/* Define constants to construct EDMA option word */* Priority, 3bit */#define C_D

43、D_C64_EDMA_OPT_URGENT_PRI (0x0)#define C_DD_C64_EDMA_OPT_HIGH_PRI (0x1)#define C_DD_C64_EDMA_OPT_MEDIUM_PRI (0x2)#define C_DD_C64_EDMA_OPT_LOW_PRI (0x3)/* Element size, 2bit */#define C_DD_C64_EDMA_OPT_ESIZE_32 (0x0)#define C_DD_C64_EDMA_OPT_ESIZE_16 (0x1)#define C_DD_C64_EDMA_OPT_ESIZE_8 (0x2)1.4 通

44、過(guò)union和struct傳遞不同格式報(bào)文網(wǎng)絡(luò)通信中進(jìn)行數(shù)據(jù)包交互時(shí)通常以固定數(shù)據(jù)區(qū)傳遞,但每次傳遞的消息類型可能不同,因此固定數(shù)據(jù)區(qū)的有效數(shù)據(jù)長(zhǎng)度可能不一樣,此時(shí)需要字段標(biāo)識(shí)消息類型和消息長(zhǎng)度,以便雙方對(duì)數(shù)據(jù)進(jìn)行解析。每種消息都有共有的消息頭開始,然后是特定的消息信息,這樣消息的結(jié)構(gòu)將變得非常清晰,統(tǒng)一的格式如下:typedef struct tag_STRU_MAT_XXX STRU_DD_MSG_HDR struMsgHdr; STRU_DD_XXX struXXX; STRU_MAT_XXX;消息頭如下:typedef struct tag_STRU_DD_MSG_HDR u8 u8O

45、pCode; u8 u8Len; u8 au8MsgHdrDataC_DD_COMM_MSG_HEADER_DATA_BYTE_LEN; STRU_DD_MSG_HDR;通過(guò)消息頭中的u8OpCode操作碼來(lái)確定消息類型,也就確定了消息的具體內(nèi)容;若對(duì)于某種消息,其有效數(shù)據(jù)長(zhǎng)度是可變的,則u8Len將標(biāo)識(shí)消息數(shù)據(jù)域的長(zhǎng)度當(dāng)消息內(nèi)容固定時(shí),最好以結(jié)構(gòu)體封裝消息內(nèi)容。typedef struct tag_STRU_MAT_SET_BPP_TOD_SFN_TSN_REQ STRU_DD_MSG_HDR struMsgHdr; u32 u32TodPart1; u32 u32TodPart2; u16

46、 u16SfnPeriod; u16 u16TsnPeriod; u16 u16Sfn; u16 u16Tsn; STRU_MAT_SET_BPP_TOD_SFN_TSN_REQ;上述方式消息內(nèi)容零散,同時(shí)也不便于將其另行傳遞給其他模塊。封裝后的結(jié)構(gòu)如下:typedef struct tag_STRU _BPP_TOD_SFN_TSN_PARAM u32 u32TodPart1; u32 u32TodPart2; u16 u16SfnPeriod; u16 u16TsnPeriod; u16 u16Sfn; u16 u16Tsn; STRU _BPP_TOD_SFN_TSN_PARAM;typ

47、edef struct tag_STRU_MAT_SET_BPP_TOD_SFN_TSN_PARAM _REQ STRU_DD_MSG_HDR struMsgHdr; STRU _BPP_TOD_SFN_TSN_ PARAM struTimeParam; STRU_MAT_SET_BPP_TOD_SFN_TSN_PARAM _REQ;不同消息STRU_MAT_XXX長(zhǎng)度不同,但又需要統(tǒng)一的數(shù)據(jù)區(qū)來(lái)存儲(chǔ),因此需要定義一個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)統(tǒng)一各種消息,關(guān)鍵在于統(tǒng)一的數(shù)據(jù)區(qū)長(zhǎng)度如何確定,其既保證可以容納所有消息類型同時(shí)又不浪費(fèi)存儲(chǔ)空間?一種方式算出各種消息占用的最大存儲(chǔ)空間,對(duì)于具體的消息數(shù)據(jù)采用純數(shù)組存

48、儲(chǔ),其根據(jù)頭部的u8OpCode確定消息類型,然后再?gòu)?qiáng)制轉(zhuǎn)換消息數(shù)據(jù)進(jìn)行解析。typedef struct tag_STRU_DD_COMM_MSG STRU_DD_MSG_HDR struMsgHdr; u32 au32CommData C_DD_COMM_MSG_MAX_DATA_WORD_LEN; STRU_DD_COMM_MSG;另外一種方式是通過(guò)union在同一片內(nèi)存空間存儲(chǔ)不同格式信息,由header來(lái)分辨消息類型和長(zhǎng)度typedef struct tag_STRU_DD_MAT_COMM_MSG STRU_DD_MSG_HDR struMsgHdr; union STRU_ BP

49、P_CUR_LOAD struCurLoad STRU _BPP_TOD_SFN_TSN_ PARAM struTimeParam; STRU_DD_BPP_LOAD_PARAM struLoadParam; unionMsgData; STRU_DD_MAT_COMM_MSG;各種具體的消息共用一塊固定長(zhǎng)度內(nèi)存,取決于最大的消息大小不用根據(jù)各種消息類型算C_DD_COMM_MSG_MAX_DATA_WORD_LEN的值,消息類型的改變不影響tag_STRU_DD_MAT_COMM_MSG,可以自適應(yīng)消息類型的變化;但添加新消息類型時(shí)需要更改STRU_DD_MAT_COMM_MSG。在傳輸鏈路

50、上進(jìn)行數(shù)據(jù)傳輸時(shí)并不是傳遞STRU_DD_MAT_COMM_MSG,而是傳遞具體的消息頭和消息內(nèi)容,因?yàn)橛行畔⒖赡懿]有這么多數(shù)據(jù),這樣可以節(jié)省傳輸時(shí)間。此時(shí)可用sizeof(STRU_MAT_XXX)或者sizeof(STRU_DD_MSG_HDR) + sizeof(STRU_DD_XXX)1.5 將相關(guān)功能變量封裝為結(jié)構(gòu)體何謂相關(guān)?是指多個(gè)不同意義的變量任何時(shí)候都是統(tǒng)一定義、統(tǒng)一初始化、統(tǒng)一傳遞給其他模塊,就好像難兄難弟一樣任何時(shí)候都在一塊,此時(shí)就應(yīng)該將這些變量組織為結(jié)構(gòu)體,優(yōu)勢(shì)在于:a) 便于管理、定義、聲明,避免零散的變量;b) 意義明確,結(jié)構(gòu)清晰;c) 函數(shù)調(diào)用時(shí)避免傳遞過(guò)多參數(shù),提高調(diào)用性能,參數(shù)少不易出錯(cuò)。不足在于對(duì)于結(jié)構(gòu)體的訪問(wèn)效率不如單獨(dú)的變量,但此性能影響很??;為了代碼更好的可讀性、可移植可維護(hù)性性和可靠性,此處結(jié)構(gòu)體的形式更合適。以

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論