Unix网络编程之IO模型
首先,我們要了解IO模型先要知道在底層操作系統是通過哪些設備來實現數據的傳輸,其次要了解IO模型中哪些是發生阻塞調用操作,然后有了上述的基本認知之后,開始來了解IO模型是如何演進,最后通過IO模型的演進我們要辨別IO模型中的關鍵術語聯系與區分,上述的思維導圖囊括以下要分享的知識點!
?
?
數據傳輸交換媒介
Unix操作系統結構圖
概要說明
-
用戶空間: 將上述用戶級別或者是unix編程的應用程序的部分稱為用戶空間,我們可以通過啟動進程來調用內核命令完成從硬件設備讀取或寫入等操作
-
系統內核: 是直接與計算機硬件打交道的應用程序級別,在計算機相關的書籍中也稱為操作系統,可以通過操作系統級別提供的一些組件來幫助用戶進程與計算機硬件完成通信交互的操作,可以稱為一個中轉站
-
硬件:?對于網絡編程而言,與關聯的硬件設備就是網絡接口控制器等設備,通過網絡接口控制器將字節流數據傳輸到互聯網再根據IP地址等信息傳輸到其他計算機系統應用程序,實現多臺計算機系統之間的通信
-
文件描述符(File descriptor):在linux/unix系統中,文件進程存儲著一份文件描述表(數組存儲),數組存儲file結構指針類型,文件進程通過數組索引來完成文件的操作,這里的數組索引也就是文件描述符,簡稱為fd
?
?
至此,通過上述的圖解以及簡要說明可知,用戶進程無法直接操作硬件設備,必須通過向內核發起請求命令,然后由內核向硬件設備解析并執行相應的命令操作,于是跨計算機的進程網絡數據傳輸需要將數據發送到系統內核再到網絡接口控制器,通過網絡接口控制器實現計算機之間的通信,如下圖所示:
?
?
網絡IO阻塞操作
IO讀取操作核心步驟
-
用戶進程向系統內核發起數據讀取操作請求,必須等待內核從硬件設備中獲取數據直到數據報準備完成
-
當內核從設備中準備好數據的時候,需要將數據報從內核復制到用戶空間中
IO阻塞操作
-
recvfrom函數:屬于IO操作的系統調用,這里區分系統內核和用戶進程,調用函數的時候需要從用戶進程的上下文空間切換到內核空間中運行,一段時間之后才能切換回來
-
select/poll函數: 允許進程指示內核等待多個事件中的任何一個發生,并只在有一個或者多個事件發生抑或是超過指定時間段便喚醒用戶進程,告知當前事件是否就緒狀態
IO操作的對比
-
recvfrom函數: 調用當前函數的時候,此時用戶進程受阻于系統內核,這個時候其他進程發起請求調用的時候須等待系統內核完成工作之外才能處理其他的進程請求,也就是1:1模型
-
select/poll函數:調用當前函數的時候,此時用戶進程受阻于當前select函數,不斷輪詢向內核select就緒狀態的描述符或者是超過指定時間段被喚醒,由于select系統函數調用是攜帶描述符集合到內核,因此可以對N個描述符進行監聽(姑且稱描述符的就緒狀態為事件可讀狀態,后面論述均以事件論述),即N:1模型,如下圖:
?
?
?
IO模型演進
基于上述的數據傳輸以及IO阻塞操作可知,網絡編程需要讀取網絡傳輸過來的數據需要先經過系統內核再到用戶空間,期間需要系統內核等待數據準備完成以及數據復制到用戶空間兩個步驟,同時為了簡化概念,將TCP的低套接字等信息隱藏不做說明,用簡單且易于理解的UDP傳輸方式來演示Unix下的5種IO模型
?
阻塞式IO模型
-
自應用進程發起recvfrom系統調用,在此期間一直處于被阻塞,因為這個時候需要等待內核獲取數據報信息復制到用戶空間中,除非被中斷異常返回,否則將一直處于阻塞狀態
-
以下是時序圖展示阻塞式IO?
非阻塞式IO模型
-
非阻塞式主要體現在用戶進程發起recvfrom系統調用的時候,這個時候系統內核還沒有接收到數據報,直接返回錯誤給用戶進程,告訴用戶進程“當前還沒有數據報可達,晚點再來”
-
用戶進程接收到信息,但是用戶進程不知道什么時候數據報可達,于是就開始不斷輪詢(polling)向系統內核發起recvfrom的系統調用“詢問數據來了沒”,如果沒有則繼續返回錯誤
-
用戶進程輪詢發起recvfrom系統調用直至數據報可達,這個時候需要等待系統內核復制數據報到用戶進程的緩沖區,復制完成之后將返回成功提示
-
以下時序圖展示NIO的方式?
IO復用模型
-
IO復用模型是使用select或者poll函數向系統內核發起調用,阻塞于這兩個之一的系統函數調用,而不是真正阻塞于實際的IO操作(recvfrom調用才是實際阻塞IO操作的系統調用)
-
阻塞于select函數的調用,等待系統內核準備數據并通知當前事件為可讀狀態
-
當select接收到系統內核通知事件為可讀狀態時,就可以向系統內核發起recvfrom調用將數據復制到用戶空間的緩沖區
-
IO復用模式時序圖如下?
信號驅動式IO模型
-
先開啟套接字的信號IO驅動功能,并通過一個內置安裝信號處理函數的signaction系統調用,當發起調用之后將會直接返回
-
其次,等待內核從網絡中接收數據報之后,向用戶進程發送當前數據可達的信號給到信號處理函數
-
信號處理函數接收到信息就發起recvfrom系統調用等待內核復制數據報到用戶空間的緩沖區
-
接收到復制完成的返回成功提示之后,應用進程就可以開始從網絡中讀取數據
-
上述是基于信號驅動式IO模型,當系統內核描述符就緒時將會發送SIGNO給到用戶進程,這個時候再發起recvfrom的系統調用等待返回成功提示,流程如下:
異步IO模型
-
由POSIX規范定義,告知系統內核啟動某個操作,并讓內核在整個操作包括數據等待以及數據復制過程的完成之后通知用戶進程數據已經準備完成,可以進行讀取數據
-
與上述的信號IO模型區分在于通過異步方式直接通知我們何時IO操作完成,而信號IO是通知我們何時可以啟動一個IO操作
-
時序圖如下?
5種IO模型分析
-
對于阻塞式IO模型,屬于1:1模型,也就是系統內核只能處理一個請求,其他請求過來的時候必須等待當前請求處理完成才能進行,性能相當差,可以通過多線程變更為N:1模型,與非阻塞式IO類似,區分在于前者多線程,后者單線程
-
對于驅動式信號IO模型,雖然是非阻塞式IO模型,但是基于內核通知回調的實現機制比較復雜(在信號函數異步處理IO與讀取數據操作要保持先后順序,個人認為信號函數正確設計是不處理業務的,即后續的數據讀取等操作)
-
而對于非阻塞式IO模型,相比阻塞式IO而言,而是通過不斷輪詢的方式發起recvfrom系統調用,由于存在內核與用戶空間的切換,會損耗性能
-
對于IO復用模型而言,阻塞于select函數的調用(本質上是基于文件描述符集合的遍歷),向內核注冊對應的事件并等待事件可讀或者超時通知到select函數
-
而對于AIO模型而言,是一種實現真正的非阻塞異步IO方式,但是在linux/unix系統支持此IO模型設計并不確定
-
目前大多數Unix/Linux服務器都是基于IO復用模型進行優化改進,即對select/poll方法進行增強優化
?
?
IO關鍵術語
同步與異步的定義
-
同步:發起一個fn的調用,需要等待調用結果返回,該調用結果要么是期望的結果要么是異常拋出的結果,可以說是原子性操作(要么成功要么失敗返回)
-
異步: 發起一個fn調用,無需等待結果就直接返回,只有當被調用者執行處理程序之后通過“喚醒”手段通知調用方獲取結果(喚醒的方式有回調,事件通知等)
-
小結: 同步和異步關注的是程序之間的通信
阻塞與非阻塞的定義
-
阻塞: 類比線程阻塞來說明,在并發多線程爭搶資源的競態條件下,如果有一個線程已持有鎖,那么當前線程將無法獲取鎖而被掛起,處于等待狀態
-
非阻塞: 一旦線程釋放鎖,其他線程將會進入就緒狀態,具備爭搶鎖的資格
-
小結: 阻塞與非阻塞更關注是程序等待結果的狀態
-
由此可知,同步異步與阻塞非阻塞之間不存在關聯,關注的目標是不一樣的
同步IO與異步IO(基于POSIX規范)
-
同步IO: 表示應用進程發起真實的IO操作請求(recvfrom)導致進程一直處于等待狀態,這時候進程被阻塞,直到IO操作完成返回成功提示
-
異步IO: 表示應用進程發起真實的IO操作請求(recvfrom),這個時候系統內核向用戶進程將直接返回一個錯誤信息,“相當于告訴進程還沒有處理好,好了會通知你”
-
阻塞IO: 主要是體現發起IO操作請求通知內核并且內核接收到到請求之后如果讓進程等待,那么就是阻塞
-
非阻塞IO: 發起IO操作請求的時候不論結果如何直接告訴進程“不用等待,晚點再來”,那就是非阻塞
IO模型對比
根據上述的同步與異步IO定義并結合上述的IO模型可知,只有異步IO模型符合POSIX規范的異步IO,其他IO模型都存在recvfrom系統調用被內核阻塞,屬于同步IO操作,由此可總結如下:
-
也就是說,要么稱為同步IO和異步IO,要么稱為上述的IO模型名稱
-
大部分操作系統都是基于同步IO的方式實現,對于支持異步IO模型的操作系統還不確定,在實際工作中接觸到的IO模型,從嚴格意義上來說應該稱為Blocking-IO(阻塞IO)和Non-Blocking-IO(非阻塞IO),而不是同步IO和異步IO
-
小結: 同步與異步針對通信機制,阻塞與非阻塞針對程序調用等待結果的狀態
總結
以上是生活随笔為你收集整理的Unix网络编程之IO模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OkHttp源码深度解析
- 下一篇: 装一颗种植牙要多少钱