收藏等會學會!IO多路復用,從來沒遇到過能講得這么明白的了

很多對技術有追求的讀者朋友,做到一定階段后都希望技術有所精進。有些讀者朋友可能會研究一些中間件的技術架構和實現原理。比如,Nginx為什么能同時支撐數萬乃至數十萬的連接?為什么單工作線程的Redis性能比多線程的Memcached還要強?Dubbo的底層實現是怎樣的,為什么它的通信效率非常高?

實際上,上面的一些問題都和網絡模型相關。本文從基礎的概念和網絡編程開始,循序漸進講解Linux五大網絡模型,包括耳熟能詳的多路復用IO模型。相信讀完本文,大家會對網絡編程和網絡模型有一個較清晰的理解。

基本概念


我們先來了解幾個基本概念。

什么是I/O?

所謂的I/O(Input/Output)操作實際上就是輸入輸出的數據傳輸行為。程序員最關注的主要是磁盤IO和網絡IO,因為這兩個IO操作和應用程序的關系最直接最緊密。

磁盤IO:磁盤的輸入輸出,比如磁盤和內存之間的數據傳輸。

網絡IO:不同系統間跨網絡的數據傳輸,比如兩個系統間的遠程接口調用。

下面這張圖展示了應用程序中發生IO的具體場景:

通過上圖,我們可以了解到IO操作發生的具體場景。一個請求過程可能會發生很多次的IO操作:

1,頁面請求到服務器會發生網絡IO

2,服務之間遠程調用會發生網絡IO

3,應用程序訪問數據庫會發生網絡IO

4,數據庫查詢或者寫入數據會發生磁盤IO

阻塞與非阻塞

所謂阻塞,就是發出一個請求不能立刻返回響應,要等所有的邏輯全處理完才能返回響應。

非阻塞反之,發出一個請求立刻返回應答,不用等處理完所有邏輯。

內核空間與用戶空間

在Linux中,應用程序穩定性遠遠比不上操作系統程序,為了保證操作系統的穩定性,Linux區分了內核空間和用戶空間??梢赃@樣理解,內核空間運行操作系統程序和驅動程序,用戶空間運行應用程序。Linux以這種方式隔離了操作系統程序和應用程序,避免了應用程序影響到操作系統自身的穩定性。這也是Linux系統超級穩定的主要原因。

所有的系統資源操作都在內核空間進行,比如讀寫磁盤文件,內存分配和回收,網絡接口調用等。所以在一次網絡IO讀取過程中,數據并不是直接從網卡讀取到用戶空間中的應用程序緩沖區,而是先從網卡拷貝到內核空間緩沖區,然后再從內核拷貝到用戶空間中的應用程序緩沖區。對于網絡IO寫入過程,過程則相反,先將數據從用戶空間中的應用程序緩沖區拷貝到內核緩沖區,再從內核緩沖區把數據通過網卡發送出去。

Socket(套接字)

Socket可以理解成,在兩個應用程序進行網絡通信時,一個應用程序將數據寫入Socket,然后通過網卡把數據發送到另外一個應用程序的Socket中。

所有的網絡協議都是基于Socket進行通信的,不管是TCP還是UDP協議,應用層的HTTP協議也不例外。這些協議都需要基于Socket實現網絡通信。5種網絡IO模型也都要基于Socket實現網絡通信。實際上,HTTP協議是建立在TCP協議之上的應用層協議。HTTP協議負責如何包裝數據,而TCP協議負責如何傳輸數據。

絕大部分編程語言,都支持Socket編程,例如Java,Php,Python等等。而這些語言的Socket SDK都是基于操作系統提供的 socket() 函數來實現的。不管是Linux還是windows,都提供了相應的 socket() 函數。

Socket 編程過程


我們來看看Socket 編程過程是怎樣的。

不管Java、Python還是Php,很多編程語言都支持Socket編程。Linux,Windows等操作系統都開放了網絡編程接口。只不過,各種編程語言對底層操作系統提供的網絡編程接口做了封裝而已。

從服務端開始,服務端首先調用 socket() 函數,按指定的網絡協議和傳輸協議創建 Socket ,例如創建一個網絡協議為 IPv4,傳輸協議為 TCP 的Socket。接著調用 bind() 函數,給這個 Socket 綁定一個 IP 地址和端口,綁定這兩個的目的是什么?

  • 綁定端口的目的:當內核收到 TCP 報文,通過 TCP 頭里的端口號,來找到我們的應用程序,然后把數據傳遞給我們
  • 綁定 IP 地址的目的:一臺機器可能有多個網卡,每個網卡都對應一個 IP 地址,只有綁定一個網卡對應的IP時,內核在收到該網卡上的包,才會發給我們的應用程序

綁定完 IP 地址和端口后,就可以調用 listen() 函數進行監聽。如果我們要判定服務器上某個網絡程序有沒有啟動,可以通過 netstat 命令查看對應的端口號是否被監聽。

服務端進入了監聽狀態后,通過調用 accept() 函數,來從內核獲取客戶端的連接,如果沒有客戶端連接,則會阻塞等待客戶端連接的到來。

那客戶端是怎么發起連接的呢?客戶端在創建好 Socket 后,調用 connect()函數發起連接,該函數的參數要指明服務端的 IP 地址和端口號,然后眾所周知的 TCP 三次握手就開始了。

連接建立后,客戶端和服務端就開始相互傳輸數據了,雙方可以通過 read()和 write() 函數來讀寫數據。

基于TCP 協議的 Socket 編程過程就結束了,整個過程如下圖所示:

網絡IO模型


5種Linux網絡IO模型包括:同步阻塞IO、同步非阻塞IO、多路復用IO、信號驅動IO和異步IO。

同步阻塞IO

我們先看一下傳統阻塞IO。在Linux中,默認情況下所有socket都是阻塞模式的。當用戶線程調用系統函數read(),內核開始準備數據(從網絡接收數據),內核準備數據完成后,數據從內核拷貝到用戶空間的應用程序緩沖區,數據拷貝完成后,請求才返回。從發起read請求到最終完成內核到應用程序的拷貝,整個過程都是阻塞的。為了提高性能,可以為每個連接都分配一個線程。因此,在大量連接的場景下就需要大量的線程,會造成巨大的性能損耗,這也是傳統阻塞IO的最大缺陷。

同步非阻塞IO

用戶線程在發起Read請求后立即返回,不用等待內核準備數據的過程。如果Read請求沒讀取到數據,用戶線程會不斷輪詢發起Read請求,直到數據到達(內核準備好數據)后才停止輪詢。非阻塞IO模型雖然避免了由于線程阻塞問題帶來的大量線程消耗,但是頻繁地重復輪詢大大增加了請求次數,對CPU消耗也比較明顯。這種模型在實際應用中很少使用。

多路復用IO模型

多路復用IO模型,建立在多路事件分離函數select,poll,epoll之上。在發起read請求前,先更新select的socket監控列表,然后等待select函數返回(此過程是阻塞的,所以說多路復用IO并非完全非阻塞)。當某個socket有數據到達時,select函數返回。此時用戶線程才正式發起read請求,讀取并處理數據。這種模式用一個專門的監視線程去檢查多個socket,如果某個socket有數據到達就交給工作線程處理。由于等待Socket數據到達過程非常耗時,所以這種方式解決了阻塞IO模型一個Socket連接就需要一個線程的問題,也不存在非阻塞IO模型忙輪詢帶來的CPU性能損耗的問題。多路復用IO模型的實際應用場景很多,比如大家耳熟能詳的Java NIO,Redis,Nginx以及Dubbo采用的通信框架Netty都采用了這種模型。

下圖是基于select函數Socket編程的詳細流程。

用一句話解釋多路復用模型。多路:可以理解成多個網絡連接(TCP連接)。復用:服務端反復使用同一個線程去監聽所有網絡連接中是否有IO事件(如果有IO事件就交給工作線程從對應的連接中讀取并處理數據)。

信號驅動IO模型

信號驅動IO模型,應用進程使用sigaction函數,內核會立即返回,也就是說內核準備數據的階段應用進程是非阻塞的。內核準備好數據后向應用進程發送SIGIO信號,接到信號后數據被復制到應用程序進程。

采用這種方式,CPU的利用率很高。不過這種模式下,在大量IO操作的情況下可能造成信號隊列溢出導致信號丟失,造成災難性后果。

異步IO模型

異步IO模型的基本機制是,應用進程告訴內核啟動某個操作,內核操作完成后再通知應用進程。在多路復用IO模型中,socket狀態事件到達,得到通知后,應用進程才開始自行讀取并處理數據。在異步IO模型中,應用進程得到通知時,內核已經讀取完數據并把數據放到了應用進程的緩沖區中,此時應用進程

直接使用數據即可。

很明顯,異步IO模型性能很高。不過到目前為止,異步IO和信號驅動IO模型應用并不多見,傳統阻塞IO和多路復用IO模型還是目前應用的主流。Linux2.6版本后才引入異步IO模型,目前很多系統對異步IO模型支持尚不成熟。很多應用場景采用多路復用IO替代異步IO模型。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373

推薦閱讀更多精彩內容