国产日韩欧美一区二区三区三州_亚洲少妇熟女av_久久久久亚洲av国产精品_波多野结衣网站一区二区_亚洲欧美色片在线91_国产亚洲精品精品国产优播av_日本一区二区三区波多野结衣 _久久国产av不卡

?

LWIP中零拷貝技術(shù)的研究與應(yīng)用

2018-07-25 12:05:48趙成青李宥謀劉永斌
關(guān)鍵詞:拷貝網(wǎng)卡內(nèi)核

趙成青,李宥謀,劉永斌,王 濤

(西安郵電大學(xué),陜西 西安 710000)

0 引 言

LWIP是瑞典計(jì)算機(jī)科學(xué)院(SICS)的Adam Dunkels開發(fā)的用于嵌入式系統(tǒng)的開源TCP/IP協(xié)議棧[1]。LWIP的含義是輕量級(jí)的TCP/IP協(xié)議,專注于減少資源消耗。嵌入式網(wǎng)絡(luò)傳輸系統(tǒng)由于成本資源的限制,往往采用簡化的TCP/IP協(xié)議。文中通過研究、分析常用的嵌入式網(wǎng)絡(luò)協(xié)議棧LWIP的結(jié)構(gòu),在物理層和應(yīng)用層提出了提高系統(tǒng)傳輸效率的改進(jìn)方法。

在小型嵌入式系統(tǒng)中,LWIP的實(shí)現(xiàn)基于上層協(xié)議已明確知道下層協(xié)議所使用的數(shù)據(jù)結(jié)構(gòu)的特點(diǎn)[2]。它會(huì)假設(shè)各層間的部分?jǐn)?shù)據(jù)結(jié)構(gòu)和實(shí)現(xiàn)原理在其他層是可見的。在數(shù)據(jù)包遞交過程中,各層協(xié)議可以通過指針直接指向數(shù)據(jù)包中其他層次的字段。所以上層可直接使用取地址計(jì)算得到下層中的數(shù)據(jù),這不僅使整個(gè)協(xié)議棧對(duì)數(shù)據(jù)包的操作更加靈活,而且避免了LWIP協(xié)議棧內(nèi)部數(shù)據(jù)遞交時(shí)的復(fù)制。但是,這僅僅是在LWIP協(xié)議棧內(nèi)部實(shí)現(xiàn)數(shù)據(jù)的零拷貝。在物理網(wǎng)卡向協(xié)議棧傳遞數(shù)據(jù)時(shí)和協(xié)議棧向應(yīng)用程序傳遞數(shù)據(jù)時(shí),還是存在兩次消耗較大的數(shù)據(jù)拷貝過程。所以,文中提出在網(wǎng)卡接收數(shù)據(jù)時(shí)讓LWIP內(nèi)核存儲(chǔ)區(qū)指針直接指向物理網(wǎng)卡的寄存器地址的方法,避免了物理網(wǎng)卡數(shù)據(jù)到LWIP協(xié)議棧緩沖區(qū)數(shù)據(jù)的拷貝[3]。在應(yīng)用層,提出了利用μcos操作系統(tǒng)的郵箱機(jī)制,避免了多個(gè)外部應(yīng)用程序和協(xié)議棧內(nèi)核交互時(shí)的數(shù)據(jù)拷貝[4],從而實(shí)現(xiàn)了從物理層到應(yīng)用層真正的數(shù)據(jù)零拷貝,并且提高了系統(tǒng)的并發(fā)性。

1 物理網(wǎng)卡到LWIP協(xié)議棧數(shù)據(jù)的傳遞

在嵌入式系統(tǒng)中應(yīng)用比較廣泛的是MicroChip公司的ENC28J60網(wǎng)卡。在網(wǎng)卡驅(qū)動(dòng)函數(shù)接收數(shù)據(jù)包時(shí),網(wǎng)卡驅(qū)動(dòng)函數(shù)首先向網(wǎng)卡發(fā)送數(shù)據(jù)包傳送指令,此時(shí)網(wǎng)卡會(huì)把BNRY(邊界寄存器)處的一個(gè)網(wǎng)卡格式的數(shù)據(jù)包數(shù)據(jù)一次性全部發(fā)送到DMA端口,此時(shí)網(wǎng)卡驅(qū)動(dòng)函數(shù)會(huì)在DMA端口讀取所有字節(jié)數(shù)據(jù)后,網(wǎng)卡會(huì)自動(dòng)將BNRY(邊界寄存器)值調(diào)整為下一個(gè)數(shù)據(jù)包地址,以準(zhǔn)備下一次讀取所有字節(jié)數(shù)據(jù)。然后將接收到的數(shù)據(jù)封裝成LWIP熟悉的格式并且寫到緩沖區(qū)中,這個(gè)過程涉及到數(shù)據(jù)到拷貝[5]。在ENC28J60網(wǎng)卡中接收緩沖器由一個(gè)硬件管理的循環(huán)FIFO構(gòu)成。ERXST表示接收緩沖區(qū)起始地址,ERXNDH表示接收緩沖區(qū)結(jié)束地址[6]。如圖1所示,通過把網(wǎng)卡相關(guān)的寄存器映射到LWIP內(nèi)核內(nèi)存空間,就可以直接對(duì)網(wǎng)卡寄存器進(jìn)行操作,避免了物理網(wǎng)卡到LWIP內(nèi)核空間的數(shù)據(jù)拷貝。然后封裝成LWIP內(nèi)核能夠識(shí)別的pbuf類型的數(shù)據(jù)包結(jié)構(gòu),在LWIP內(nèi)核中由low_level_input()函數(shù)完成這個(gè)功能。通過ehternetif_input()函數(shù)解析該數(shù)據(jù)包的類型,然后將該數(shù)據(jù)包指針遞交給相應(yīng)的上層。

2 LWIP協(xié)議層間數(shù)據(jù)傳遞

在網(wǎng)卡接收數(shù)據(jù)時(shí),需要申請(qǐng)一個(gè)數(shù)據(jù)包,然后將網(wǎng)卡中的數(shù)據(jù)填入數(shù)據(jù)包中。發(fā)送數(shù)據(jù)包時(shí),協(xié)議棧的某層中會(huì)申請(qǐng)一個(gè)pbuf,并將相應(yīng)的數(shù)據(jù)裝入到數(shù)據(jù)區(qū)域,同時(shí)相關(guān)的協(xié)議首部信息也會(huì)被填寫到pbuf的預(yù)留數(shù)據(jù)區(qū)域中[7]。數(shù)據(jù)包申請(qǐng)函數(shù)有兩個(gè)重要參數(shù),一個(gè)是想申請(qǐng)的數(shù)據(jù)包pbuf類型,另一個(gè)重要參數(shù)是該數(shù)據(jù)包是在協(xié)議棧中哪一層被申請(qǐng)的,分配函數(shù)會(huì)根據(jù)這個(gè)層次的不同,在pbuf數(shù)據(jù)區(qū)域前為相應(yīng)的協(xié)議預(yù)留出首部空間,這就是offset值??偟膩碚f,LWIP定義了四個(gè)層次,當(dāng)數(shù)據(jù)包申請(qǐng)時(shí),所處的層次不同,會(huì)導(dǎo)致預(yù)留空間的offset值不同[8]。層次定義時(shí)通過一個(gè)枚舉類型pbuf_layer:

Typedef enum{

PBUF_TRANSPORT,//傳輸層

PBUF_IP,//網(wǎng)絡(luò)層

PBUF_LINK,//鏈路層

PBUF_RAW,//原始層

}pbuf_layer;

上面的結(jié)構(gòu)體中除了定義枚舉類型pbuf_layer來表示各個(gè)網(wǎng)絡(luò)協(xié)議層的名稱外,還定義了兩個(gè)宏:PBUF_TRANSPORT_HLEN和PBUF_IP_HLEN。前者是典型的TCP報(bào)文首部長度,而后者是典型的不帶任何選項(xiàng)字段的IP首部長度。代碼如下所示:

Switch(layer){

Case PBUF_TRANSPORT:

Offset=PBUF_LINK_HLEN+PBUF_IP_HLEN+PBUF_TRASNSPORT_HLEN;

Case PBUF_IP:

Offset=PBUF_LINK_HLEN+PBUF_IP_HLEN;

Case PBUF_LINK:

Offset=PBUF_LINK_HLEN;

Case PBUF_LINK:

Offset=0;

在LWIP的數(shù)據(jù)包管理函數(shù)pbuf.c中,首先根據(jù)數(shù)據(jù)包申請(qǐng)時(shí)傳入的協(xié)議層參數(shù),計(jì)算需要在pbuf數(shù)據(jù)區(qū)簽預(yù)留的長度offset值,然后根據(jù)pbuf的類型進(jìn)行實(shí)際申請(qǐng)。Pbuf_pooll類型申請(qǐng)最復(fù)雜[9],因?yàn)榭赡苄枰獛讉€(gè)pool連接在一起,以此來滿足用戶的空間需求。

限于篇幅,對(duì)LWIP內(nèi)存分配機(jī)制不做深入研究;pbuf_ref和pbuf_rom類型申請(qǐng)最簡單,它們只是在內(nèi)存MEMEP_PBUF中分配一個(gè)pbuf結(jié)構(gòu)空間,然后初始化相關(guān)字段,注意這兩種類型的payload指針需要用戶自行設(shè)置,通常在調(diào)用完函數(shù)pbuf_alloc后,調(diào)用者需要將payload指向某個(gè)數(shù)據(jù)區(qū)。

在原始層以太網(wǎng)驅(qū)動(dòng)中:

P=pbuf_alloc(PBUF_RAW,recvlen,PBUF_RAM);

這個(gè)調(diào)用語句申請(qǐng)了一個(gè)PBUF_RAM類型的pbuf,且其申請(qǐng)的協(xié)議層為PBUF_RAW,所以pbuf_alloc函數(shù)不會(huì)在數(shù)據(jù)區(qū)前預(yù)留出任何首部空間;通過使用p->payload,就可以實(shí)現(xiàn)對(duì)pbuf中數(shù)據(jù)區(qū)的讀取或者寫入操作了。

在傳輸層TCP層:

P=pbuf_alloc(PBUF_RAW,recvlen,PBUF_RAM);

它告訴數(shù)據(jù)包分配函數(shù)使用PBUF_RAM類型的pbuf,且數(shù)據(jù)前應(yīng)該預(yù)留一部分的首部空間。由于這里是PBUF_TRANSPORT層,所以預(yù)留空間將有54個(gè)字節(jié),即TCP首部長度的20個(gè)字節(jié)、IP數(shù)據(jù)包首部長度的20個(gè)字節(jié)以及以太網(wǎng)幀首部長度的14字節(jié)。當(dāng)數(shù)據(jù)包往下層遞交,各層協(xié)議就直接操作這些預(yù)留空間的數(shù)據(jù),以實(shí)現(xiàn)數(shù)據(jù)首部的填寫,這樣就避免了數(shù)據(jù)的拷貝。

3 LWIP軟件與用戶程序間的數(shù)據(jù)傳遞

3.1 用戶緩沖數(shù)據(jù)結(jié)構(gòu)

協(xié)議棧API實(shí)現(xiàn)時(shí),也為用戶提供了數(shù)據(jù)包管理函數(shù),可以完成數(shù)據(jù)包內(nèi)存申請(qǐng)、釋放、數(shù)據(jù)拷貝等任務(wù)。無論是UDP還是TCP連接,當(dāng)協(xié)議棧接收到數(shù)據(jù)包后,會(huì)將數(shù)據(jù)封裝在一個(gè)netbuf中,并遞交給應(yīng)用程序[10]。在發(fā)送數(shù)據(jù)時(shí),不同類型的連接將導(dǎo)致不同的數(shù)據(jù)處理方式。對(duì)于TCP連接,內(nèi)核會(huì)根據(jù)用戶提供待發(fā)送數(shù)據(jù)的起始數(shù)據(jù)和長度,自動(dòng)將數(shù)據(jù)封裝在合適的數(shù)據(jù)包中,然后放入發(fā)送隊(duì)列;對(duì)于UDP,用戶需要手動(dòng)將數(shù)據(jù)封裝在netbuf中,通過調(diào)用發(fā)送函數(shù),內(nèi)核直接發(fā)送數(shù)據(jù)包中的數(shù)據(jù)段。

應(yīng)用程序使用netbuf結(jié)構(gòu)來描述、組裝數(shù)據(jù)包,該結(jié)構(gòu)只是對(duì)內(nèi)核pbuf的簡單封裝,是用戶應(yīng)用程序和協(xié)議棧共享的。外部應(yīng)用程序可以使用該結(jié)構(gòu)來管理發(fā)送數(shù)據(jù)、接收數(shù)據(jù)的緩沖區(qū)。netbuf是基于pbuf實(shí)現(xiàn)的,其結(jié)構(gòu)如以下代碼所示:

Struct netbuf{

Struct pbuf *p,*ptr;

Ip_addr_t *addr;

U16_t port;

}

其中,netbuf相當(dāng)于一個(gè)數(shù)據(jù)首部,保存數(shù)據(jù)的字段是p,它指向pbuf鏈表首部,ptr指向鏈表中的其他位置,addr表示IP地址,port表示端口號(hào)。

netbuf是應(yīng)用程序描述待發(fā)送數(shù)據(jù)和已接收數(shù)據(jù)的基本結(jié)構(gòu),引入netbuf結(jié)構(gòu)看似會(huì)讓應(yīng)用程序更加繁雜,但實(shí)際上內(nèi)核為應(yīng)用程序提供了API,通過共享一個(gè)netbuf結(jié)構(gòu)(如圖2所示),兩部分API就能實(shí)現(xiàn)對(duì)數(shù)據(jù)包的共同處理,避免了數(shù)據(jù)拷貝。

圖2 用戶緩沖區(qū)結(jié)構(gòu)

3.2 操作數(shù)據(jù)緩沖區(qū)指針

與BSD相同,LWIP協(xié)議棧API也對(duì)網(wǎng)絡(luò)連接進(jìn)行了抽象。但它們之間的抽象存在一定的差別:BSD實(shí)現(xiàn)了更高級(jí)別的抽象,用戶可以像操作文件那樣來操作一個(gè)網(wǎng)絡(luò)連接;LWIP中,API只能實(shí)現(xiàn)較低級(jí)別的抽象,用戶操作的僅僅是一個(gè)網(wǎng)絡(luò)連接,而不是文件。在BSD中,應(yīng)用程序處理的網(wǎng)絡(luò)數(shù)據(jù)都處于一片連續(xù)的存儲(chǔ)區(qū)域中,可以使用戶對(duì)數(shù)據(jù)的處理更加方便。在LWIP中,若API使用上述數(shù)據(jù)存儲(chǔ)機(jī)制可能會(huì)導(dǎo)致很大的缺陷,因?yàn)長WIP中網(wǎng)絡(luò)數(shù)據(jù)都存儲(chǔ)在pbuf中,如果要實(shí)現(xiàn)存儲(chǔ)在連續(xù)的存儲(chǔ)區(qū)的話,需要將所有pbuf數(shù)據(jù)拷貝到這個(gè)連續(xù)的存儲(chǔ)中,這將造成數(shù)據(jù)的拷貝。為了避免數(shù)據(jù)拷貝以后再遞交給用戶,需要直接操作pbuf的一些方法,而LWIP中恰恰提供了這些方法。比如通過netbuf_next()可以修改數(shù)據(jù)指針指向下一個(gè)數(shù)據(jù)段,如果返回值為0,表示netbuf中還存在數(shù)據(jù)段,大于0說明指針已經(jīng)指向netbuf中的最后一個(gè)數(shù)據(jù)段了,小于0表明netbuf中已經(jīng)沒有數(shù)據(jù)段了。當(dāng)用戶未調(diào)用netbuf_next()函數(shù)的情況下,ptr和p都默認(rèn)指向第一個(gè)pbuf。通過netbuf_next()對(duì)協(xié)議棧和應(yīng)用程序共同緩沖區(qū)指針的調(diào)整和讀取,避免了應(yīng)用程序和數(shù)據(jù)以及內(nèi)核棧的拷貝。

4 應(yīng)對(duì)多個(gè)外部應(yīng)用程序的指針傳遞

在單獨(dú)運(yùn)行LWIP時(shí),用戶應(yīng)用程序和協(xié)議棧內(nèi)核處于同一進(jìn)程中,用戶程序通過回調(diào)的方式進(jìn)行。這樣,用戶程序和協(xié)議棧內(nèi)核出現(xiàn)了相互制約的關(guān)系,因?yàn)橛脩舫绦驁?zhí)行的時(shí)候,內(nèi)核一直處于等待狀態(tài),內(nèi)核需要等待用戶函數(shù)返回一個(gè)處理結(jié)果再繼續(xù)執(zhí)行。如果用戶執(zhí)行計(jì)算量很大,執(zhí)行時(shí)間很長,則協(xié)議棧代碼就一直得不到執(zhí)行,協(xié)議棧接收,處理數(shù)據(jù)包效率會(huì)受到直接的影響。最嚴(yán)重的結(jié)果是,如果發(fā)送方速度很快,則協(xié)議棧會(huì)因?yàn)閬聿患疤幚矶霈F(xiàn)丟包的情況。

為了設(shè)計(jì)多進(jìn)程外部應(yīng)用程序,將LWIP移植到μcos操作系統(tǒng)下,讓LWIP內(nèi)核作為操作系統(tǒng)的一個(gè)任務(wù)運(yùn)行[11]。LWIP協(xié)議棧設(shè)計(jì)時(shí),提供了協(xié)議棧與操作系統(tǒng)之間函數(shù)的接口。協(xié)議棧API由兩部分組成。一部分提供給應(yīng)用程序,一部分提供給協(xié)議棧內(nèi)核。應(yīng)用程序和協(xié)議棧內(nèi)核通過進(jìn)程間通信機(jī)制進(jìn)行通信和同步[12]。使用到的進(jìn)程通信機(jī)制包括了以下三種[13]:

(1)郵箱,例如內(nèi)核郵箱mbox、連接上接收數(shù)據(jù)的郵箱recvmbox;

(2)信號(hào)量,例如op_completed,用于兩部分API同步;

(3)共享內(nèi)存,例如內(nèi)核消息結(jié)構(gòu)tcp_msg、API消息內(nèi)容api_msg等[14]。

兩部分API間的關(guān)系如圖3所示。API設(shè)計(jì)的主要思想是讓應(yīng)用程序成為一個(gè)單獨(dú)的進(jìn)程;而協(xié)議棧也成為一個(gè)單獨(dú)的進(jìn)程。用戶進(jìn)程只負(fù)責(zé)數(shù)據(jù)的計(jì)算等其他工作,協(xié)議棧進(jìn)程僅僅負(fù)責(zé)通信工作。兩部分進(jìn)程之間使用三種IPC方式中的郵箱和信號(hào)量集,內(nèi)核進(jìn)程可以直接將數(shù)據(jù)遞交到應(yīng)用程序郵箱中,然后繼續(xù)執(zhí)行,不必阻塞等待,郵箱對(duì)于應(yīng)用程序來說就像一個(gè)輸入隊(duì)列,提高了系統(tǒng)的實(shí)時(shí)性[15]。

圖3 兩部分獨(dú)立進(jìn)程間的通信

全局郵箱mbox在協(xié)議棧初始化時(shí)建立,用于內(nèi)核進(jìn)程tcpip_thread接收消息。內(nèi)核進(jìn)程通過共享內(nèi)存的方式與協(xié)議棧的其他各個(gè)模塊進(jìn)行通信,它從郵箱中獲得的是一個(gè)指向消息結(jié)構(gòu)的指針。函數(shù)tcp_input在內(nèi)存池中為系統(tǒng)消息結(jié)構(gòu)申請(qǐng)空間,并根據(jù)消息類型初始化結(jié)構(gòu)中的相關(guān)字段,把內(nèi)核消息封裝在tcp_msg結(jié)構(gòu)中,最后將消息投遞到系統(tǒng)郵箱中等待內(nèi)核進(jìn)程tcpip_thread處理。tcpip_thread使用從郵箱中獲得的指針指定到對(duì)應(yīng)內(nèi)存地址處讀取消息內(nèi)容,從而避免了兩個(gè)進(jìn)程間通信的數(shù)據(jù)的拷貝。

5 性能測(cè)試對(duì)比及其應(yīng)用

在局域網(wǎng)內(nèi),對(duì)ARM開發(fā)板STM32F103VET6-EV上基于無操作系統(tǒng)和移植了μcos操作系統(tǒng)的LWIP兩種方法編寫的UDP服務(wù)器進(jìn)行數(shù)據(jù)吞吐能力的測(cè)試,以此來估算網(wǎng)卡及整個(gè)板子的網(wǎng)絡(luò)處理性能及對(duì)比無操作系統(tǒng)模擬層和在操作系統(tǒng)模擬層下編寫的UDP服務(wù)器性能的差別。

在Windows主機(jī)上運(yùn)行iperf軟件來測(cè)試服務(wù)器的數(shù)據(jù)吞吐能力。如圖4(a)所示,在軟件上選擇UDP協(xié)議,設(shè)置好服務(wù)器IP地址(192.168.1.230)和端口號(hào)(5000)后,單擊start iperf,軟件開始對(duì)服務(wù)器性能進(jìn)行測(cè)試。從圖4(a)可以看出,服務(wù)器的上下行帶寬都可以維持在9 800 kb/s左右,很接近ENC28J60網(wǎng)卡的處理值上線10 M/s。在操作系統(tǒng)模擬層下基于LWIP零拷貝技術(shù)編寫的UDP服務(wù)器,板子的網(wǎng)絡(luò)處理性能達(dá)到最優(yōu)。從圖4(b)可以看出,基于無操作系統(tǒng)模擬層下編程的服務(wù)器在客戶端連續(xù)發(fā)送大量數(shù)據(jù)時(shí)導(dǎo)致丟包情況,嚴(yán)重情況下甚至出現(xiàn)死機(jī)的情況。

圖4 UDP性能測(cè)試

6 結(jié)束語

綜上所述,在應(yīng)對(duì)多個(gè)外部應(yīng)用程序的情況下,無操作系統(tǒng)模擬層的UDP服務(wù)器編程,雖然避免了數(shù)據(jù)的拷貝,但是無法應(yīng)對(duì)多個(gè)外部應(yīng)用程序。所以將LWIP移植到μcos操作系統(tǒng)下,不僅減少了內(nèi)存開銷,而且能夠應(yīng)對(duì)多個(gè)外部應(yīng)用程序。文中的研究成果已經(jīng)成功應(yīng)用于嵌入式網(wǎng)管系統(tǒng)項(xiàng)目并實(shí)際運(yùn)行,不僅提高了基于STM32平臺(tái)μcos操作系統(tǒng)下測(cè)量儀器代理模塊的傳輸效率,提高了系統(tǒng)的實(shí)時(shí)性,而且節(jié)約了內(nèi)存開銷。

猜你喜歡
拷貝網(wǎng)卡內(nèi)核
在DDS 中間件上實(shí)現(xiàn)雙冗余網(wǎng)卡切換的方法
萬物皆可IP的時(shí)代,我們當(dāng)夯實(shí)的IP內(nèi)核是什么?
強(qiáng)化『高新』內(nèi)核 打造農(nóng)業(yè)『硅谷』
Server 2016網(wǎng)卡組合模式
基于嵌入式Linux內(nèi)核的自恢復(fù)設(shè)計(jì)
Linux內(nèi)核mmap保護(hù)機(jī)制研究
中國生殖健康(2018年1期)2018-11-06 07:14:38
挑戰(zhàn)Killer網(wǎng)卡Realtek網(wǎng)游專用Dragon網(wǎng)卡
文件拷貝誰最“給力”
巧識(shí)劣質(zhì)水晶頭
洪湖市| 济源市| 浦北县| 称多县| 福泉市| 修武县| 娄烦县| 广东省| 连城县| 卓资县| 永川市| 通州区| 钟祥市| 广南县| 巴马| 张家港市| 孝感市| 赤水市| 班戈县| 大港区| 望都县| 平遥县| 陇川县| 胶南市| 万全县| 华亭县| 兴海县| 称多县| 漯河市| 遵化市| 马边| 晋江市| 镇原县| 饶阳县| 镇平县| 苏尼特右旗| 察隅县| 抚顺县| 闸北区| 东乌珠穆沁旗| 宁都县|