常見網路加速技術淺談(一)
來自專欄軟體定義網路66 人贊了文章
TCP/IP協議棧簡介
當用戶需要向網路發送數據的時候,用戶實際上是通過應用程序來完成這項工作。應用程序向一個描述了對端連接的文件描述符(File Description)寫數據。
之後位於操作系統內核的TCP/IP協議棧,從文件描述符收到數據,完成TCP分段(如果是TCP連接的話),加TCP,IP,Ethernet Header。在加這些Header的時候,也涉及到一些內容的計算,例如校驗和,序列號。
最後,操作系統內核通過網卡的驅動,告知網卡需要發送的數據,這裡的數據是長度合適,並且封裝了各種協議頭的網路數據。網卡會再加一些其他數據確保傳輸的可靠性。最後,網路數據由網卡從網線(如果是有線連接的話)發出去,經過各個網路轉發設備送到對端。
對端,也就是網路數據的接收端,有個類似的過程,不過方向是反的。網卡從網線接收到數據,通知系統內核來取數據,位於系統內核的TCP/IP協議棧完成校驗,剝離TCP、IP、Ethernet頭部,拼接數據。最後將完整的數據傳遞給應用程序,或者說最終用戶。用戶程序仍然是通過一個文件描述符讀取數據。
所以,可以將網路傳輸在操作系統內的整個過程分為三個部分:
- User area:應用程序發送和接收數據
- Kernel area:TCP/IP協議棧和系統內核對用戶數據的封裝解封裝
- Device area:網卡實際的發送並接收網路數據
從前面的描述可以看出,User area和Device area的工作都相對簡單,而對於複雜的網路協議的處理主要在Kernel area。Kernel area的任何處理都是需要CPU完成的。很顯然,如果單位時間要傳遞的數據越多,CPU需要進行的運算就越多。
網路帶寬這些年有了很大的提升,乙太網從最開始的10M到現在100G,提升了一萬倍。雖然CPU這些年也有很大的發展,但是單核CPU的頻率並沒有提升這麼多。有人可能會說CPU的核數增加了很多,但是把一個網路數據流交給多個CPU核心去處理本身有一定的挑戰,另一方面計算機的需要處理的任務越來越複雜,尤其是引入了虛擬化之後,計算機上不僅跑應用程序,還需要跑容器,虛擬機,CPU本身的負荷可能就已經很重。
乙太網速度的提升大於CPU的計算速度的提升,使得CPU能夠用來處理單個網路包的時間變少了。如果CPU不能及時處理網路數據,那必然會影響網路傳輸的延時(latency)和吞吐量(throughput)。因此需要一些技術/方案來降低CPU處理單個網路包的時間。這次就過一下現在計算機系統中常見的網路加速技術,當然還是淺談。
DMA
DMA全稱是Direct Memory Access。DMA可以同時應用於網路數據的發送和接收。DMA本身是一個通用的技術,它有一個獨立於CPU的DMA控制器。在數據拷貝的時候,CPU只需要告訴DMA控制器,拷貝數據的起始地址,數據長度,之後將匯流排控制權交給DMA控制器,就可以不需要CPU的介入,完成數據拷貝。
使用DMA,在網卡從內存拷貝數據(發送),和網卡向內存拷貝數據(接收)時,只需要很少的CPU介入。
RSS
RSS全稱是Receive Side Scaling,從名字上可以看出,這項加速技術只在網路數據接收時有效。具備RSS能力的網卡,有多個接收隊列,網卡可以用不同的接收隊列來接收不同的網路流,再將這些隊列分配到不同的CPU核上進行處理,充分利用多核處理器的能力,將網路數據接收的負荷分散開,從而提高網路傳輸的效率。
RSS雖然能更好的利用多核CPU,但是一方面,網路數據的分發需要考慮TCP連接,NUMA等因素,本身較為複雜。另一方面,它增加了網路傳輸對CPU的影響,前面說過計算機本身有計算任務,不可能只用來收發網路數據。在實際使用的時候,通常會將RSS限定在有限的幾個CPU核上,以隔離網路傳輸帶來的CPU影響。
NAPI
NAPI全稱是New API,這是Linux系統針對網路接收的優化。硬體I/O與CPU的交互一般有中斷和輪詢兩種方式。中斷的CPU代價較大,但是實時性好,且不需要CPU一直值守,而輪詢需要CPU定期查詢I/O,需要CPU一直值守,並且不是真正的實時。對於網卡來說,一個繁忙的網路,每次網路數據包到達,如果都採用中斷,這樣頻繁的中斷會影響系統的整體效率。而對於一個流量小的網路,如果採用輪詢,一個是實時性差,會導致延時(Latency)上升,另一方面CPU需要一直值守,CPU效率低。
NAPI根據不同的場景,採用不同的方式作為CPU和網卡的交互方法,在大網路流量的時候,採用輪詢的方式,讀取網卡數據,小網路流量的時候則採用中斷的方式,從而提高CPU的效率。
Checksum offload
很多網路協議,例如IP、TCP、UDP都有自己的校驗和(checksum)。傳統上,校驗和的計算(發送數據包)和驗證(接收數據包)是通過CPU完成的。這對CPU的影響很大,因為校驗和需要每個位元組的數據都參與計算。對於一個100G帶寬的網路,需要CPU最多每秒計算大約12G的數據。
為了減輕這部分的影響,現在的網卡,都支持校驗和的計算和驗證。系統內核在封裝網路數據包的時候,可以跳過校驗和。網卡收到網路數據包之後,根據網路協議的規則,進行計算,再將校驗和填入相應的位置。
因為Checksum offload的存在,在用tcpdump之類的抓包分析工具時,有時會發現抓到的包提示校驗和錯誤(checksum incorrect)。tcpdump抓到的網路包就是系統內核發給網卡的網路包,如果校驗和放到網卡去計算,那麼tcpdump抓到包的時刻,校驗和還沒有被計算出來,自然看到的是錯誤的值。
Scatter/Gather
這項加速只能用於網路數據的發送。Scatter/Gather本身也是操作系統裡面一個通用的技術,也叫做vector addressing。簡單來說,就是數據在傳輸的過程中,數據的讀取方,不需要從一段連續的內存讀取數據,而是可以從多個離散的內存地址讀取數據。例如,系統內核在收到應用程序傳來的原始數據時,可以保持這段數據不動。之後在另一塊內存中計算出各層協議的Header。最後通知網卡驅動,從這兩塊內存中將數據拷貝過去。SG可以減少不必要的內存拷貝操作。
SG需要Checksum offload的支持,因為現在數據是離散的,系統內核不太容易計算Checksum。
TSO
TSO全稱是TCP Segmentation Offload,它只能用於網路數據的發送。從名字可以看出,這是一個與TCP協議緊密相關的方法。
應用程序可以傳遞任意長度數據給TCP。TCP位於傳輸層並不會直接將整段用戶數據交給下層協議去傳輸。因為TCP本身是一個可靠的傳輸協議,而下層協議,IP/Ethernet都不是可靠的,下層協議在數據傳輸過程中可能會丟失數據。TCP不僅需要確保傳輸的可靠性,為了保證效率,還需要盡量提高傳輸的成功率。TCP的辦法就是化整為零,各個擊破。
在開始後面的描述之前,先說兩個相近且容易混淆的詞。一個是Segmentation(分段),一個是Fragmentation(分片)。TCP協議在將用戶數據傳給IP層之前,會先將大段的數據根據MSS(Maximum Segment Size)分成多個小段,這個過程是Segmentation,分出來的數據是Segments。IP協議因為MTU(Maximum Transmission Unit)的限制,會將上層傳過來的並且超過MTU的數據,分成多個分片,這個過程是Fragmentation,分出來的數據是Fragments。這兩個過程都是大塊的數據分成多個小塊數據,區別就是一個在TCP(L4),一個在IP(L3)完成。
接著回來,如果TCP直接傳輸整段數據給下層協議,假設是15000位元組的用戶數據,網卡的MTU是1500,考慮到Header,IP層會將數據分成11個IP Fragments在網路上傳輸,為了描述簡單,我們就假設分成了10個IP Fragments。假設每個IP packet的傳輸成功率是90%,因為TCP協議有自己的校驗和,在數據的接收端,IP協議必須將完整的15000位元組的用戶數據收完,並且拼接傳給TCP,才算接收端成功收到數據。這樣的話,傳輸一次成功傳輸的概率是(90%)^10=34%。一旦TCP接收端沒有成功收到數據,發送端就需要重新將整段數據15000位元組再發一次。假設發送4次,也就是總共60000位元組,傳輸的成功率能上升到80%。
如果TCP協議本身就將數據分成小段,一段一段傳輸呢?前面說過,TCP是根據MSS完成Segmentation,MSS通常是根據MTU計算,以確保一個TCP Segment不必在IP協議層再進行Fragmentation。為了描述簡單,我們還是拋開網路協議的頭部,現在TCP將應用層的15000位元組數據在自己這裡分成了10個Segments。每個Segment對應一個IP packet,成功率還是90%。如果Segment發送失敗了,TCP只需要重傳當前Segment,之前已經成功發送的TCP Segment不必重傳。這樣,對於每個Segment,只要發送2次成功率就能達到99%。假設每個Segment發送2次,相應的應用層數據總共發送2次,也就是30000位元組,傳輸的成功率可以達到(99%)^10=90%。也就是說TCP Segmentation之後再傳輸,需要發送的數據量更少,成功率反而更高。當然實際中,因為TCP Segmentation,會對每個TCP Segment增加TCP 頭部,相應傳輸的數據會更多一點,但是前面的分析結果不受這點數據量的影響。所以,TCP Segmentation對於TCP的可靠來說是必須的。
但同時,它也有自身的缺點。TCP Segmentation之後,相當於對於一段數據,分成了若干個TCP Segments,每個Segment都有自己的TCP頭部,這若干個TCP頭部,都需要CPU去計算checksum,sequence等。同時,每個TCP Segment還會有自己的IP協議頭部,也需要CPU去計算IP協議頭部的內容。所以可以預見的是,TCP Segmentation之後,CPU的負擔增加了許多。
TSO就是將TCP Segmentation的工作,卸載(offload)到網卡來完成。有了TSO,操作系統只需要傳給硬體網卡一個大的TCP數據(當然是包在Ethernet Header和IP Header內,且不超過64K)。網卡會代替TCP/IP協議棧完成TCP Segmentation。這樣,就消除了TCP Segmentation帶來的CPU負擔。
另一個好處在DMA。雖然說每次DMA操作,不需要CPU太多的介入,但是仍然需要CPU配置DMA控制器。DMA的特點在於,無論傳輸數據的長短,配置工作量是一樣的。如果系統內核自己完成TCP Segmentation,那麼就有若干個TCP Segments需要通過DMA傳給網卡。而採用TSO,因為傳輸的是一大段數據,只需要配置一次DMA,就可以將數據拷貝到網卡。這也一定程度減輕了CPU的負擔。
支持TSO的網卡,仍然會按照TCP/IP協議將網路數據包生成好並發送出去。對於外界系統來說,感受不到TSO的存在。下圖是TSO和非TSO下,TCP/IP協議棧對網路數據的處理過程對比。
TSO帶來的提升是明顯的,一方面,更多的CPU被釋放出來完成別的工作,另一方面,網路吞吐量(throughput)不受CPU負荷的影響,如果沒有TSO,當CPU性能不好或者CPU本身負荷已經較大時,CPU將來不及處理足夠的網路數據,會導致網路吞吐量下降,延時上升。
TSO需要SG和Checksum offload的支持。因為TCP/IP協議棧並不知道最終的網路數據包是什麼樣,自然也沒辦法完成校驗和計算。
小結
以上是Linux系統和周邊硬體針對網路加速的一些方案,主要思想還是減少CPU處理網路數據包的時間,最開始我們說過,網路傳輸的核心問題是CPU可以用來處理每個網路包的時間變短了,所以減少CPU處理網路數據包所需的時間是最直觀的解決方法。下一篇我們再看看另一種思路的幾個相應技術方案,參考的一些鏈接也一起放到下一篇。
推薦閱讀:
※用 Squid 搭建本地 CDN 緩存代理爲網頁提速
※計算機四級網路工程師速通攻略
※網路編程
※python3 爬取半次元cosplay圖片
※網路掩碼那些事兒