高性能網絡通訊原理

目錄

高性能網絡通訊原理

前言

本來想對netty的源碼進行學習和探究,但是在寫netty之前許多底層的知識和原理性的東西理解清楚,那麼對學習網絡通訊框架的效果則會事半功倍。

本篇主要探討高性能網絡通訊框架的一些必要知識和底層操作系統相關的原理。在探討如何做之前,我們先討論下為什麼要做。

隨着互聯網的高速發展,用戶量呈指數形式遞增,從原來的PC普及到現在的移動設備普及。用戶量都是千萬甚至億為單位計算,尤其是實時通訊軟件,在線實時互動的應用出現,在線用戶數從原來的幾十上百到後來的上萬甚至上千萬。單台服務的性能瓶頸和網絡通訊瓶頸慢慢呈現。應用架構從單應用到應用數據分離,再到分佈式集群高可用架構。單台服務的性能不足可以通過構建服務集群的方式水平擴展,應用性能瓶頸被很好的解決。但是橫向擴展帶來了直接的經濟成本。

一個高性能的網絡通訊框架從硬件設備到操作系統內核以及用戶模式都需要精心設計。只要有任何地方有疏漏都會出現短板效應。

I/O訪問

當我們在讀取socket數據時,雖然我們在代碼僅僅是調用了一個Read操作,但是實際操作系統層面做了許多事情。首先操作系統需要從用戶模式轉換為內核模式,處理器會通過網卡驅動對網卡控制器進行操作,網卡控制器則控制網卡。

處理器不會直接操控硬件。

為了提高CPU利用率,I/O訪問方式也發生了很大變化。

  1. 早期的CPU直接控制外圍設備,後來增加了控制器或I/O模塊。處理器開始將I/O操作從外部設備接口分離出來。處理器通過向I/O模塊發送命令執行I/O指令。然而當I/O操作完成時並不會通知處理器I/O,因此處理器需要定時檢查I/O模塊的狀態,它會進行忙等待,因此效率並不高。
  2. 後來CPU支持了中斷方式,處理器無需等待執行I/O操作,通過中斷控制器產生中斷信號通知I/O操作完成,大大的提高了處理器利用效率。這時的I/O操作使用特定的in/out(I/O端口)指令或直接讀寫內存的方式(內存映射I/O)。但是這些方式都需要處理器使用I/O寄存器逐個內存單元進行訪問,效率並不高,在I/O操作時需要消耗的CPU時鐘周期。
  3. 為了提高效率,後來增加了DMA控制器,它可以模擬處理起獲得內存總線控制權,進行I/O的讀寫。當處理器將控制權交給DMA控制器之後,DMA處理器會先讓I/O硬件設備將數據放到I/O硬件的緩衝區中,然後DMA控制器就可以開始傳輸數據了。在此過程中處理器無需消耗時鐘周期。當DMA操作完成時,會通過中斷操作通知處理器。

I/O訪問的發展趨勢是盡可能減少處理器干涉I/O操作,讓CPU從I/O任務中解脫出來,讓處理器可以去做其他事情,從而提高性能。

對於I/O訪問感興趣的同學可以看《操作系統精髓與設計原理(第5版)》第十一章I/O管理相關內容和《WINDOWS內核原理與實現》第六章I/O論述相關內容

I/O模型

在討論I/O模型之前,首先引出一個叫做C10K的問題。在早期的I/O模型使用的是同步阻塞模型,當接收到一個新的TCP連接時,就需要分配一個線程。因此隨着連接增加線程增多,頻繁的內存複製,上下文切換帶來的性能損耗導致性能不佳。因此如何使得單機網絡併發連接數達到10K成為通訊開發者熱門的討論話題。

同步阻塞

前面提到,在最原始的I/O模型中,對文件設備數據的讀寫需要同步等待操作系統內核,即使文件設備並沒有數據可讀,線程也會被阻塞住,雖然阻塞時不佔用CPU始終周期,但是若需要支持併發連接,則必須啟用大量的線程,即每個連接一個線程。這樣必不可少的會造成線程大量的上下文切換,隨着併發量的增高,性能越來越差。

select模型/poll模型

為了解決同步阻塞帶來線程過多導致的性能問題,同步非阻塞方案產生。通過一個線程不斷的判斷文件句柄數組是否有準備就緒的文件設備,這樣就不需要每個線程同步等待,減少了大量線程,降低了線程上下文切換帶來的性能損失,提高了線程利用率。這種方式也稱為I/O多路復用技術。但是由於數組是有數組長度上限的(linux默認是1024),而且select模型需要對數組進行遍歷,因此時間複雜度是\(O_{(n)}\)因此當高併發量的時候,select模型性能會越來越差。

poll模型和select模型類似,但是它使用鏈表存儲而非數組存儲,解決了併發上限的限制,但是並沒有解決select模型的高併發性能底下的根本問題。

epoll模型

在linux2.6支持了epoll模型,epoll模型解決了select模型的性能瓶頸問題。它通過註冊回調事件的方式,當數據可讀寫時,將其加入到通過回調方式,將其加入到一個可讀寫事件的隊列中。這樣每次用戶獲取時不需要遍歷所有句柄,時間複雜度降低為\(O_{(1)}\)。因此epoll不會隨着併發量的增加而性能降低。隨着epoll模型的出現C10K的問題已經完美解決。

異步I/O模型

前面講的幾種模型都是同步I/O模型,異步I/O模型指的是發生數據讀寫時完全不同步阻塞等待,換句話來說就是數據從網卡傳輸到用戶空間的過程時完全異步的,不用阻塞CPU。為了更詳細的說明同步I/O與異步I/O的區別,接下來舉一個實際例子。

當應用程序需要從網卡讀取數據時,首先需要分配一個用戶內存空間用來保存需要讀取的數據。操作系統內核會調用網卡緩衝區讀取數據到內核空間的緩衝區,然後再複製到用戶空間。在這個過程中,同步阻塞I/O在數據讀取到用戶空間之前都會被阻塞,同步非阻塞I/O只知道數據已就緒,但是從內核空間緩衝區拷貝到用戶空間時,線程依然會被阻塞。而異步I/O模型在接收到I/O完成通知時,數據已經傳輸到用戶空間。因此整個I/O操作都是完全異步的,因此異步I/O模型的性能是最佳的。

在我的另一篇文章對windows操作系統I/O原理做了簡要的敘述,感興趣的同學可以看下。

I/O線程模型

從線程模型上常見的線程模型有Reactor模型和Proactor模型,無論是哪種線程模型都使用I/O多路復用技術,使用一個線程將I/O讀寫操作轉變為讀寫事件,我們將這個線程稱之為多路分離器。

對應上I/O模型,Reacor模型屬於同步I/O模型,Proactor模型屬於異步I/O模型。

Reactor模型

在Reactor中,需要先註冊事件就緒事件,網卡接收到數據時,DMA將數據從網卡緩衝區傳輸到內核緩衝區時,就會通知多路分離器讀事件就緒,此時我們需要從內核空間讀取到用戶空間。

同步I/O採用緩衝I/O的方式,首先內核會從申請一個內存空間用於存放輸入或輸出緩衝區,數據都會先緩存在該緩衝區。

Proactor模型

Proactor模型,需要先註冊I/O完成事件,同時申請一片用戶空間用於存儲待接收的數據。調用讀操作,當網卡接收到數據時,DMA將數據從網卡緩衝區直接傳輸到用戶緩衝區,然後產生完成通知,讀操作即完成。

異步I/O採用直接輸入I/O或直接輸出I/O,用戶緩存地址會傳遞給設備驅動程序,數據會直接從用戶緩衝區讀取或直接寫入用戶緩衝區,相比緩衝I/O減少內存複製。

總結

本文通過I/O訪問方式,I/O模型,線程模型三個方面解釋了操作系統為實現高性能I/O做了哪些事情,通過提高CPU使用效率,減少內存複製是提高性能的關鍵點。

參考文檔

  1. 《操作系統精髓與設計原理(第5版)》
  2. 《WINDOWS內核原理與實現》

微信掃一掃二維碼關注訂閱號傑哥技術分享
出處:
作者:傑哥很忙
本文使用「CC BY 4.0」創作共享協議。歡迎轉載,請在明顯位置給出出處及鏈接。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

您可能也會喜歡…