學會Zynq(10)lwIP簡介

FPGADesigner發表於2019-03-20

從本篇開始,將花大量篇幅介紹Zynq在裸機環境下乙太網的使用。裸機時最方便的就是使用SDK已經整合了的lwIP 1.4.1庫,我們將先了解lwIP的相關知識,然後再以例項的方式學習TCP、UDP的程式設計方法。


研究背景

在過去幾年裡,將計算機和計算機支援的裝置連線到無線網路的需求逐漸增長。計算機與日常裝置之間的整合度越來越高,價格也在下降。同時,藍芽、IEEE 802.11b/g(俗稱“wifi”)等無線網路技術已經非常普遍。這導致在醫療保健、安全保障、交通業、加工業等領域出現了許多新穎的場景。感測器等小型裝置可以連線到現有的網路設施中(如全球網際網路),人們可以在任何地方監控它們。

網際網路技術非常靈活,能夠適應過去幾十年不斷變化的網路環境。網際網路技術雖然最初是為阿帕網(ARPANET)等低速網路開發的,但現在可以在一個很大的鏈路技術頻譜上執行,在頻寬和誤位元速率方面由截然不同的特性。由於現在已經開發了大量使用網際網路技術的應用程式,能在未來的無線網路中使用現有的網際網路技術是非常有利的。諸如感測器之類的小型裝置通常需要體積小且價格便宜,因此不得不在有限的計算資源和記憶體上實現網際網路協議。

lwIP最早由Adam Dunkels編寫,目前由Kieran Mansley帶領的團隊開發(開發者主頁http://savannah.nongnu.org/projects/lwip )。lwIP是一個小型TCP/IP棧,可以在嵌入式系統中使用。lwIP採取模組化設計,核心棧是IP協議的實現,使用者可以在其上選擇新增TCP、UDP、DHCP等其它協議,包括這些協議的各種特性。當然這樣會導致程式碼量增加、複雜性提高,需要根據使用者的需求進行調整。此外,lwIP在有無作業系統、支援或不支援執行緒的情況下都可以執行,適用於8位或32位微處理器,支援小端和大端系統。


支援協議

在這裡插入圖片描述
lwIP是模組化設計,且支援多種協議,大部分協議在無需使用時可以將其移除,減小程式碼量。lwIP支援的鏈路層和網路層協議包括:

  • ARP:一種鏈路層協議,用於將本地硬體地址(即MAC地址)轉譯為IP地址。
  • IPv4:目前網際網路中使用的主要的網路層協議。
  • IPv6:Ipv4的下一代,IP地址大小擴充套件到了128位。
  • ICMP:一種IP的控制協議。
  • IGMP:一種IP中多點傳播的管理協議。

lwIP支援的傳輸層協議包括:

  • UDP:一種沒有可靠性機制的無連線socket協議。
  • TCP:一種面向連線的“流”協議。

lwIP支援的高層次協議包括:

  • DHCP:一種帶伺服器的IP地址獲取方法。
  • AUTOIP:一種沒有伺服器的IP地址選擇方法。
  • SNMP:用於監控網路狀況。
  • PPP:在兩個節點之間建立直接連線。

應用程式介面

lwIP提供了三種應用程式介面(Xilinx只支援RAW API和socket API兩種),用於程式和TCP/IP程式碼之間的通訊:

  • 低層次的、基於“核”和“回撥”的RAW API
  • 兩種高層次的、基於“順序”的API:netconn APIsocket API

順序型API類似於BSD socket API,執行模型是基於阻塞的開-讀-寫-關(open-read-write-close)模式。由於TCP/IP棧本質上是基於事件的,所以TCP/IP程式碼和應用程式必須在不同的執行緒中。API之間的差別如下,據此選擇使用哪種API:

  • netconn API和raw API只能用於lwIP中,用這些API編寫的程式碼不能移植到其它棧中重用。
  • socket API的目的是能與其它posix作業系統/棧之間可移植,但會降低吞吐量。
  • socket API和netconn API是需要執行緒的順序型API
  • raw API基於回撥機制,比如當新資料到達時呼叫已註冊的回撥函式。由於它不需要切換執行緒,所以由最佳的效能表現。
  • raw API和netconn API支援TX和RX的“零拷貝(zero-copy)”。

帶或不帶作業系統的lwIP

lwIP可以在裸機環境下執行,也可以在多執行緒作業系統中執行。

當在沒有作業系統的單執行緒環境中執行lwIP時,只需要IP、ICMP、UDP和TCP協議的實現、緩衝區和記憶體管理等核心元件,當然也可以新增DHCP、DNS等元件,但它們不是必須的。甚至可以只編譯UDP或TCP。在Xilinx中使用的lwIP,如果執行TCP,需要每250ms呼叫依次tcp_tmr,它會執行重傳等TCP定時器的處理工作。UDP則沒有必要。

單執行緒的主迴圈中需要呼叫鏈路層驅動程式(Xilinx介面卡有專用函式),依次處理IP資料包,然後呼叫上層協議處理程式,最後呼叫應用程式的回撥函式。

多執行緒系統中應用程式在併發執行緒中執行。可以讓所有TCP/IP的處理在一個執行緒中完成,使用者應用程式的執行緒通過API函式與TCP/IP執行緒通訊。


最大化吞吐量

一般設計者想盡可能減少程式碼量,並讓lwIP工作在最大吞吐量狀態,有諸多原因會影響到使用lwIP的乙太網裝置的效能。架構設計方面包括:

  • 由於網路位元組順序是大端模式,因此最好選擇大端模式的系統,可以省略其中的轉換。
  • 系統的一個瓶頸是乙太網MAC驅動程式(lwIP中稱作netif-driver),應儘可能使用中斷和DMA。通常驅動程式可以編寫為偏向於TX或RX,如果應用程式中某個傳輸方向更重要,應確保在高負載情況下首選該方向。在硬體允許的情況下,確保驅動程式支援分散收集(scatter-gather)。
  • 另一個瓶頸是TCP和UDP的校驗和計算,傳送資料時生成校驗和,接收資料時檢查校驗和。如果硬體支援,則將校驗和的生成和檢驗留給硬體完成。如果硬體不支援,則確保有一個優化的計算校驗和的軟體架構。

對於在Xilinx中實現,第一點我們無法選擇,第二點Xilinx已經幫你解決了。我們需要注意的就是第三點。lwIP的配置也會影響到吞吐量效能,SDK中如何配置的相關部分檢視本系列其它文章。

如果希望最大化吞吐量,應用程式應該使用RAW API,而不是netconn/socket API。設計程式前,我們首先要選擇使用UDP還是TCP。

  • UDP:優點是開銷更少,設計者自己選擇訊息大小;缺點是沒有提供安全的通訊路徑,該協議不能通知使用者對方是否收到了訊息。
  • TCP:優點是提供了一個安全的通訊路徑,當對方成功收到訊息時使用者會收到通知;缺點是開銷更大,還會自動選擇訊息大小。

選定協議後,設計者要決定應用程式如何通過網路傳遞資料:

  • UDP:確保傳遞的資料塊不會小於網路所允許的最大資料包,比如在標準乙太網中,使用udp_send一次傳送1472個位元組,以最大化一個包中資料位元組和報頭位元組的比,同時最小化網路中包間的間隔。
  • TCP:雖然TCP可以將多個tcp_write呼叫的資料合併到一個包中,但由於這個包被分割到多個pbuf中,可能會降低效能。由於TCP需要將資料包儲存起來重新傳輸,直到遠端主機發出應答訊號,所以在tcp_write/tcp_output返回後花費幾秒的時間。如果要傳送小塊資料,應該關掉nagle演算法,讓堆疊立即傳送資料,而不是等待更多資料形成更大的資料包後才傳送資料。應該避免傳送小塊資料,總是等待應答會降低效能。

相關文章