,
(西安工程大學(xué) 電子信息學(xué)院,西安 710048)
隨著工業(yè) 4.0 的提出和工業(yè)以太網(wǎng)的普及,越來(lái)越多的工業(yè)設(shè)備急需接入 Internet[14-15]。 以太網(wǎng)是嵌入式系統(tǒng)的一個(gè)重要模塊[8]。實(shí)現(xiàn)嵌入式設(shè)備的互聯(lián)必須要有網(wǎng)絡(luò)協(xié)議的支持[4]。LwIP在嵌入式通信中占有很重要的地位,主要是由于其占用資源較少。實(shí)驗(yàn)表明,運(yùn)行該協(xié)議只需要20 KB的RAM和40 KB的ROM[2]。LwIP協(xié)議為數(shù)據(jù)通信提供了基礎(chǔ)[5]。pbuf結(jié)構(gòu)對(duì)于LwIP而言是非常重要的,它關(guān)系到數(shù)據(jù)存放的問(wèn)題,由于不同類(lèi)型pbuf的內(nèi)存分配方式有所不同,那就意味著將會(huì)儲(chǔ)存不同類(lèi)型的數(shù)據(jù)包。如果對(duì)pbuf沒(méi)有一個(gè)深刻的了解和認(rèn)識(shí),就不能靈活地運(yùn)用pbuf的類(lèi)型。靈活運(yùn)用pbuf的類(lèi)型對(duì)于數(shù)據(jù)包的接收和發(fā)送是非常重要的[13],如果不能合理地分配儲(chǔ)存空間的大小和pbuf類(lèi)型,可能導(dǎo)致數(shù)據(jù)丟失和空間的浪費(fèi)。為了解決上述問(wèn)題,本文對(duì)pbuf結(jié)構(gòu)進(jìn)行了深度分析,并設(shè)計(jì)了內(nèi)存分配實(shí)驗(yàn)。
LwIP是瑞士計(jì)算機(jī)科學(xué)院開(kāi)發(fā)的一套用于嵌入式系統(tǒng)的開(kāi)源TCP/IP協(xié)議棧[7,12],用于嵌入式系統(tǒng)的開(kāi)放源代碼的 TCP/IP 協(xié)議棧[8]。 LwIP提供一系列函數(shù)實(shí)現(xiàn)IP、TCP、UDP等操作[10]。對(duì)數(shù)據(jù)包如何進(jìn)行處理,在LwIP“輕型IP協(xié)議(Light Weight IP)”[21]中是非常重要的一個(gè)環(huán)節(jié)。在網(wǎng)卡驅(qū)動(dòng)編寫(xiě)時(shí)最核心的部分是數(shù)據(jù)的存儲(chǔ)與傳輸方式[6]。而在LwIP中主要利用4種不同類(lèi)型的pbuf來(lái)對(duì)數(shù)據(jù)包進(jìn)行分類(lèi)管理。
由于實(shí)際存入pbuf結(jié)構(gòu)的數(shù)據(jù)可能會(huì)很大,所以需要由多個(gè)pbuf才可以實(shí)現(xiàn)對(duì)數(shù)據(jù)包的存放。這些pbuf可以是相同的類(lèi)型,也可以是多種類(lèi)型的混合使用。pbuf的申請(qǐng)和釋放是通過(guò)兩個(gè)函數(shù)pbuf_alloc()和pbuf_free()來(lái)完成的,pbuf_alloc()函數(shù)中含有兩個(gè)重要的參數(shù):layer和type,其中枚舉類(lèi)型pbuf_layer通過(guò)參數(shù)layer決定是協(xié)議棧的哪一層(PBUF_TRANSPORT、PBUF_IP、PBUF_LINK以及PBUF_RAW)和pbuf中的offset“偏移量”。pbuf的類(lèi)型主要通過(guò)枚舉類(lèi)型Pbuf_type體現(xiàn)出來(lái),然后根據(jù)參數(shù)type的不同進(jìn)行不同的選擇,最后再通過(guò)switch語(yǔ)句選擇用哪一種類(lèi)型。LwIP中的 pbuf 有4種類(lèi)型: PBUF_POOL、PBUF_RAM、PBUF_ROM、PBUF_REF[1]。pbuf結(jié)構(gòu)如下:
struct pbuf {
struct pbuf *next; /*指向下一個(gè)pbuf*/
void *payload; /*指向數(shù)據(jù)緩沖區(qū)*/
u16_t tot_len; /*所有pbuf的數(shù)據(jù)長(zhǎng)度*/
u16_t len; /*當(dāng)前pbuf的數(shù)據(jù)長(zhǎng)度*/
u8_t type; /*pbuf類(lèi)型*/
u8_t flags /*狀態(tài)標(biāo)志*/
u16_t ref; /*記錄當(dāng)前pbuf被引用次數(shù)*/
};
PBUF_RAM類(lèi)型的pbuf是從LwIP的內(nèi)存堆中申請(qǐng)得到的,主要用來(lái)儲(chǔ)存協(xié)議棧和應(yīng)用程序中的待發(fā)送數(shù)據(jù),所以在學(xué)習(xí)和了解PBUF_RAM之前要先對(duì)內(nèi)存堆的結(jié)構(gòu)進(jìn)行分析和了解。從圖 1內(nèi)存堆的結(jié)構(gòu)示意圖中可以看出,每一個(gè)被分配的存儲(chǔ)塊都包含一個(gè)小的結(jié)構(gòu)。其結(jié)構(gòu)體的成員變量分別包括next、prev以及used。為了能夠更加清楚地了解內(nèi)存堆的分配狀況,特意對(duì)其組織結(jié)構(gòu)圖進(jìn)行了顏色區(qū)別,其中陰影部分表示被分配(used 被置位為1),否則為0。內(nèi)存堆組織結(jié)構(gòu)還可以通過(guò)*next和*prev將相鄰的結(jié)構(gòu)塊連接起來(lái)。在內(nèi)存堆分配內(nèi)存時(shí)需要注意:在其分配時(shí)每個(gè)內(nèi)存塊的大小是有一定差異的,而且可能會(huì)隨時(shí)變動(dòng)。在對(duì)內(nèi)存堆分析過(guò)后,再對(duì)PBUF_RAM類(lèi)型的pbuf進(jìn)行代碼講解,其源代碼主要在源文件pbuf.c中。其部分代碼如下:
case PBUF_RAM:
p=(structpbuf*)mem_malloc(LwIP_MEM_ALIGN_S
IZE(SIZEOF_STRUCT_PBU+offset)+LwIP_MEM_ALIGN_SIZE(length));
/*獲取PBUF_RAM類(lèi)型內(nèi)存大小*/
if (p == NULL) {return NULL;}
p->payload = LwIP_MEM_ALIGN((void *)((u8_t *)
p+SIZEOF_STRUCT_PBUF+offset));
p->len = p->tot_len = length;/*數(shù)據(jù)包幀的大小*/
p->next = NULL;
p->type = type;
break;
圖2 PBUF_RAM類(lèi)型的pbuf結(jié)構(gòu)
通過(guò)源代碼可知,PBUF_RAM類(lèi)型的pbuf結(jié)構(gòu)內(nèi)存的大小分別包括offset、length和SIZEOF_STRUCT_PBU。其中offset表示一個(gè)偏移量里面用來(lái)存儲(chǔ)一些首部字段,如TCP報(bào)文首部、IP首部等。由于next和payload占用4個(gè)字,然而tot_len、len和ref占用2個(gè)字節(jié),type和flags為1個(gè)字節(jié),因?yàn)閳D2中每一行分配為4個(gè)字節(jié),所以next和payload各占用一行,tot_len和len兩者共同占用一行,而type、flags和ref三者共同占用一行,最后的空白區(qū)域表示數(shù)據(jù)存儲(chǔ)區(qū)。又因?yàn)槠湎群箜樞虿煌?,最終申請(qǐng)的PBUF_RAM類(lèi)型的pbuf結(jié)構(gòu)如圖2所示。
PBUF_POOL類(lèi)型的pbuf與PBUF_RAM類(lèi)似,都包括offset、length和SIZEOF_STRUCT_PBU,不同之處在于兩者申請(qǐng)內(nèi)存的地方不同,從而導(dǎo)致特性不同,由于分配PBUF_POOL速度快,所以可以用來(lái)存放對(duì)分配內(nèi)存速度要求較高的數(shù)據(jù)。其部分代碼如下:
p=(structpbuf*)memp_malloc(MEMP_PBUF_POOL);
/*獲取PBUF_POOL內(nèi)存大小*/
if (p == NULL) {
PBUF_POOL_IS_EMPTY();
return NULL;}
p->type = type;
p->next = NULL;
p->payload = LwIP_MEM_ALIGN((void *)((u8_t *)
p + (SIZEOF_STRUCT_PBUF + offset)));
break;
由于PBUF_POOL類(lèi)型的pbuf其內(nèi)存是從MEMP_PBUF_POOL中申請(qǐng)得到的,所以能夠通過(guò)在MEMP_PBUF_POOL中改變宏定義PBUF_POOL_SIZE的大小來(lái)設(shè)置內(nèi)存池的個(gè)數(shù),通過(guò)改變PBUF_POOL_BUFSIZE的數(shù)值來(lái)改變內(nèi)存池大小。但是其不能設(shè)置的太大,要根據(jù)數(shù)據(jù)實(shí)際應(yīng)用的大小來(lái)定義,因?yàn)槊總€(gè)POOL都具有固定大小,如果設(shè)置太大會(huì)造成內(nèi)存的浪費(fèi)[16],如果設(shè)置太小可能會(huì)出現(xiàn)以下問(wèn)題:
① 每個(gè)pbuf結(jié)構(gòu)在內(nèi)存池中占用的內(nèi)存大小是一定的,假如POOL設(shè)置太小,就會(huì)導(dǎo)致內(nèi)存利用率下降;
② 如果pbuf中數(shù)據(jù)區(qū)分配太小,可能會(huì)導(dǎo)致數(shù)據(jù)分組首部信息得存放到下一個(gè)pbuf結(jié)構(gòu)中,從而導(dǎo)致操作不方便。
事實(shí)上,應(yīng)用程序發(fā)送和接收的數(shù)據(jù)可能會(huì)遠(yuǎn)遠(yuǎn)大于一個(gè)PBUF_POOL所存儲(chǔ)的數(shù)據(jù)量,而且內(nèi)存池類(lèi)型的內(nèi)存分配每次分配到的大小是固定的,圖3所示一般需要多個(gè)PBUF_POOL類(lèi)型并通過(guò)指針next指向下一個(gè)PBUF_POOL類(lèi)型,從而使多個(gè)PBUF_POOL類(lèi)型鏈接成一個(gè)鏈表,用于存儲(chǔ)數(shù)據(jù)分組。雖然經(jīng)過(guò)多次分配構(gòu)成一個(gè)鏈表,但是它們?nèi)匀皇且粋€(gè)數(shù)據(jù)包,因此只有第一個(gè)pbuf有offset來(lái)存儲(chǔ)有關(guān)數(shù)據(jù)包的信息,其余的則不需要。
圖3 PBUF_POOL
圖4 PBUF_ROM和PBUF_REF
PBUF_ROM、PBUF_REF與PBUF_POOL一樣都是從內(nèi)存池中申請(qǐng)得到的,但是不同之處是它們使用的是內(nèi)存池MEMP_PBUF。如圖4所示,這兩種類(lèi)型的pbuf所申請(qǐng)的內(nèi)存主要是用來(lái)存放pbuf結(jié)構(gòu)體,并沒(méi)有給數(shù)據(jù)空間申請(qǐng)內(nèi)存,但是這兩個(gè)的數(shù)據(jù)空間可以應(yīng)用其它地方的內(nèi)存(RAM/ROM)進(jìn)行數(shù)據(jù)存儲(chǔ)。
在對(duì)pbuf進(jìn)行內(nèi)存釋放的時(shí)候是通過(guò)調(diào)用pbuf_free()來(lái)完成,當(dāng)釋放pbuf的時(shí)候,LwIP會(huì)自動(dòng)檢測(cè)pbuf的類(lèi)型,然后再調(diào)用相關(guān)的函數(shù)進(jìn)行相對(duì)應(yīng)的刪除。但是在對(duì)其內(nèi)存進(jìn)行釋放的時(shí)候需要先滿足一定的條件,其中主要是通過(guò)檢測(cè)ref“引用(reference)”的大小,只有當(dāng)ref的數(shù)值不小于1的時(shí)候,pbuf才有可能被釋放,但不是一定會(huì)被釋放,當(dāng)有其它類(lèi)型的pbuf通過(guò)next指針指向該pbuf的時(shí)候,其相對(duì)應(yīng)的ref的值就會(huì)相對(duì)應(yīng)的增加1,當(dāng)釋放pbuf的時(shí)候其先對(duì)應(yīng)的那個(gè)ref數(shù)值也會(huì)相對(duì)應(yīng)的減去1。當(dāng)有多個(gè)pbuf結(jié)構(gòu)連接在一起時(shí),釋放第一個(gè)pbuf時(shí),該pbuf鏈的第二個(gè)節(jié)點(diǎn)就會(huì)成為pbuf鏈的第一個(gè)節(jié)點(diǎn)同時(shí)會(huì)自動(dòng)檢測(cè)pbuf節(jié)點(diǎn)是否被其它的所引用,即判斷ref是否大于1,如果不大于則繼續(xù)刪除,否則終止。
本實(shí)驗(yàn)是在以Cortex-M3為內(nèi)核的開(kāi)發(fā)板上進(jìn)行[9],對(duì)PBUF_RAM類(lèi)型的pbuf在RAM中的內(nèi)存分配和釋放進(jìn)行模擬實(shí)驗(yàn),探究了其如何進(jìn)行內(nèi)存分配和存儲(chǔ)數(shù)據(jù)以及如何進(jìn)行內(nèi)存的釋放。因?yàn)閜buf的類(lèi)型較多,而且在對(duì)數(shù)據(jù)管理的時(shí)候可能為多種類(lèi)型混合使用,不易針對(duì)某個(gè)類(lèi)型的pbuf進(jìn)行單獨(dú)內(nèi)存分配和釋放實(shí)驗(yàn)。對(duì)該內(nèi)存堆的操作類(lèi)似于C語(yǔ)言中的malloc/free[1,3]。所以該實(shí)驗(yàn)主要是通過(guò)運(yùn)用malloc/free函數(shù)在內(nèi)部?jī)?nèi)存RAM分配和釋放內(nèi)存的方式來(lái)模擬PBUF_RAM在RAM中的分配和釋放[16-18],并通過(guò)LCD進(jìn)行字符串的存儲(chǔ)地址、寫(xiě)入的字符串,以及RAM使用率的顯示。
其中圖5、圖6中的SRAMIN USED字樣表示RAM的使用率,Addr用來(lái)表示數(shù)據(jù)存放的地址。通過(guò)圖5可以看出,當(dāng)向內(nèi)部?jī)?nèi)存RAM中寫(xiě)入字符串“Memory Malloc Test123”時(shí),其內(nèi)存使用率為10%,存儲(chǔ)地址為0X2000 92C0,說(shuō)明已經(jīng)將字符串寫(xiě)入到RAM中。圖6是對(duì)其內(nèi)存釋放,可以看出其內(nèi)存使用率減小,其地址變?yōu)?X0000 0000,表明將RAM中的數(shù)據(jù)進(jìn)行了釋放,但是其內(nèi)存使用率理論上應(yīng)該為0,這里的5%就是內(nèi)存碎片。由于嵌入式系統(tǒng)的內(nèi)存空間是非常寶貴的,尤其對(duì)于數(shù)據(jù)的傳輸而言,如果每次傳輸都產(chǎn)生碎片,那就是資源的浪費(fèi)。所以對(duì)pbuf結(jié)構(gòu)的分析與探究是非常必要的。
圖5 內(nèi)存申請(qǐng)
圖6 內(nèi)存釋放
[1] 付曉軍,夏應(yīng)清,何軒.嵌入式LwIP協(xié)議棧的內(nèi)存管理[J].電子技術(shù)應(yīng)用,2006(3):56-57.
[2] 來(lái)愛(ài)華,盧軍,游繼安.基于LwIP協(xié)議的多點(diǎn)控制系統(tǒng)研究[J].湖北工程學(xué)院學(xué)報(bào),2016,36(3):28-33.
[3] 蔡雄飛,王新華,郭淑琴.嵌入式TCP/IP協(xié)議LwIP的內(nèi)存管理機(jī)制研究[J].杭州電子科技大學(xué)報(bào),2012,32(4):118-121.
[4] 王祖云,楊思國(guó),王建偉,等.嵌入式LwIP協(xié)議棧的移植與測(cè)試研究[J].計(jì)算機(jī)與數(shù)字工程,2014,42(2):272-275,318.
[5] 趙智增,馮春鵬.基于STM32的以太網(wǎng)接口轉(zhuǎn)多串口透?jìng)髂K設(shè)計(jì)[J]. 山西電子技術(shù),2016(2):30-33.
[6] 曹紹華,史永宏.基于32位處理器的網(wǎng)絡(luò)驅(qū)動(dòng)及協(xié)議棧研究[J].現(xiàn)代電子技術(shù),2016,39(19):317-321.
[7] 張齊,勞熾元.輕量級(jí)協(xié)議棧LwIP的分析與改進(jìn)[J].計(jì)算機(jī)工程與設(shè)計(jì),2010,31(10):2169-2171,2256.
[8] 韓德強(qiáng),楊淇善,王宗俠,等.基于μC/OS-III的LwIP協(xié)議棧的移植與實(shí)現(xiàn)[J].電子技術(shù)應(yīng)用,2013,39(5):18-21.
[9] 楊明極,祝慶峰,李碩.基于STM32的嵌入式網(wǎng)絡(luò)控制器設(shè)計(jì)[J].測(cè)控技術(shù),2014,33(10):93-96.
[10] 徐立艷.基于ARM和LabVIEW的網(wǎng)絡(luò)數(shù)據(jù)采集測(cè)試系統(tǒng)設(shè)計(jì)[J].現(xiàn)代電子技術(shù),2016,39(5):24-27,32.
[11] 高羅卿,莊源昌.基于LwIP協(xié)議的嵌入式遠(yuǎn)程監(jiān)控終端的研發(fā)與實(shí)現(xiàn)[J].電氣自動(dòng)化,2015,37(1):49-51.
[12] 劉培剛,杜靖中.基于μC/OS-II和LwIP嵌入式設(shè)備以太網(wǎng)通信研究與實(shí)現(xiàn)[J].電子設(shè)計(jì)工程,2017,25(16):129-133.
[13] 薛建彬,郭燕波,許洋,等.LwIP在微控制系統(tǒng)中的移植與應(yīng)用[J].數(shù)字技術(shù)與應(yīng)用,2016(10):2.
[14] 周一兵,劉憲鵬.LwIP 在嵌入式系統(tǒng)中的應(yīng)用[J].科技視界,2013(6):40.
[15] 曹輝.基于μC/OS-III的嵌入式web服務(wù)器的應(yīng)用研究[J].自動(dòng)化技術(shù)與應(yīng)用,2016,35(2):36-39.
[16] 鄢濤,于曦.基于C++的高效內(nèi)存池的設(shè)計(jì)與實(shí)現(xiàn)[J].成都大學(xué)學(xué)報(bào):自然科學(xué)版,2017,36(3):257-261.
[17] Bryant R E.深入理解計(jì)算機(jī)系統(tǒng)[M].龔奕利,等譯.北京:北京機(jī)械工業(yè)出版社,2016.
[18] Wang N,Liu X,He J,et al.Collaborative memory pool inCluster system[C]//International Conference on ParallelProcessing,007.China:Xi'an,2007.
[19] 侯捷.STL 源碼剖析[M].武漢:華中科技大學(xué)出版社,2002.
[20] HAN D,YANG Q,WANG Z,et al.Implementationof LwIP porting based on μC/OS-III[J].Application of Electronic Technique,2013.
[21] 蔣俊,鐘偉勝.μC/OS-II和LwIP的并發(fā)服務(wù)器與代理線程設(shè)計(jì)模式[J].單片機(jī)與嵌入式系統(tǒng)應(yīng)用,2014(12):42-44.
徐健(副教授),主要研究方向?yàn)殡娔苜|(zhì)量、數(shù)字信號(hào)處理;孫慶,主要研究方向?yàn)橛?jì)算機(jī)控制網(wǎng)絡(luò)。