這是【NIO系列】第二篇,歡迎持續關注:java
【NIO系列】——之TCP探祕linux
上一篇咱們講到了關於TCP/IP協議的一些內容,這些是網絡編程的必備知識。在瞭解NIO以前咱們必需要了解一下對應的系統層IO模型,好比java的NIO對應是那種IO模型,阻塞和同步的差別在哪裏,又是否相同。瞭解了這些更方便咱們的後續的NIO探解。編程
同步、異步,阻塞、非阻塞,這四種狀態常有人分不清,主要是這四種狀態的定義自己也不是很明確,因此各類解答的方式都有。常見的分類有如下:網絡
同步阻塞IO架構
同步非阻塞IO異步
異步非阻塞IOsocket
針對某種IO模型,咱們如何分類,能夠基於POSIX對同步/異步的定義來判別:async
- A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes;函數
- An asynchronous I/O operation does not cause the requesting process to be blocked;spa
那麼從上咱們能夠看出:
阻塞:是否阻塞主要體如今調用的線程是否能夠幹別的,關注的是程序的等待狀態
同步:是否同步體如今消息通訊機制上 。
也就是說同步和異步說的是消息的通知機制,阻塞非阻塞說的是線程的狀態 。
若是說以上的定義依然沒法判別,咱們能夠從輸入操做的兩個階段來看:
通常來講,一個輸入操做一般包括兩個不一樣階段:
(1)等待數據準備好;(2)從內核向進程複製數據。
是否同步的判斷依據是:是否針對的是整個過程,也就是2個階段,是否有阻塞。
是否阻塞的判斷依據是:按程序(線程)等待消息通知時的狀態角度來講的,也就是主要是針對第一階段來講。
咱們舉例來講:
好比說作飯這件事,通常要分爲連個步驟。
一、買菜,準備食材
二、炒菜,作出飯菜
方案一:本身動手處理。
一、去超市買菜,準備食材(阻塞,當前時段只能作一件事,且須要持續的等待)
二、回家切菜,炒菜,作出美味飯菜(阻塞,仍是本身來處理)
評價: 方案一同步阻塞。首先階段一是阻塞的,因此認定爲阻塞,兩個階段都是阻塞的,認定爲同步的。
方案二:盒馬配送食材,本身作飯
一、網上下單,盒馬配送食材,快遞到了會敲門聯繫你。(非阻塞的,這期間你能夠幹其餘事)
二、拿到菜,切菜、炒菜,作出美味飯菜(阻塞)
評價:方案二爲同步非阻塞。階段一爲非阻塞,認定爲非阻塞。階段二爲阻塞,兩階段中有一個爲阻塞,認定爲同步。
方案三:盒馬配送,請阿姨作飯
一、網上下單,盒馬配送食材,快遞到了會敲門聯繫你。(非阻塞的,這期間你能夠幹其餘事)
二、網上請阿姨小時工,幫忙作這一餐,作好通知我。(非阻塞,期間能夠幹其餘事)
評價:方案三爲異步非阻塞。階段一爲非阻塞,認定爲非阻塞。階段二非阻塞,則兩階段中都沒有阻塞,認定爲異步。
那麼是否有異步阻塞IO模型,沒有,要記得異步狀態是包含二個階段的,若是有阻塞的過程,爲什麼還叫異步?
網上有不少介紹有異步阻塞模型的,我目前查到的資料尚未這個證實,如有找到相關論文,還請指教。目前我認爲沒有這個模型的。
在《UNIX網絡編程:卷一》的第六章書中列出了五種IO模型:
阻塞式I/O;
非阻塞式I/O;
I/O複用(select,poll,epoll...);
信號驅動式I/O(SIGIO);
異步I/O(POSIX的aio_系列函數);
同步阻塞 IO 模型是最經常使用的一個模型,也是最簡單的模型。在linux中,默認狀況下全部的socket都是blocking
。它符合人們最多見的思考邏輯。
在這個IO模型中,用戶空間的應用程序執行一個系統調用(recvform),這會致使應用程序阻塞,什麼也不幹,直到數據準備好,等待kernel準備好從網絡上接收到的數據報 + 等待收到的報文被從kernel複製到buf中,recvfrom方法纔會返回,最後進程再處理數據。
這就是阻塞式IO模型
非阻塞IO時對一個非阻塞描述符循環調用recvfrom,持續的輪詢(polling),以查看某個操做是否就緒。與阻塞IO不同,"非阻塞將大的整片時間的阻塞分紅N多的小的阻塞, 因此進程不斷地有機會 '被' CPU光顧"。
非阻塞的recvform系統調用調用以後,進程並無被阻塞,內核立刻返回給進程,若是數據還沒準備好,此時會返回一個error。進程在返回以後,能夠乾點別的事情,而後再發起recvform系統調用。如此循環的進行recvform系統調用,檢查內核數據,直到數據準備好,再拷貝數據到進程。拷貝數據整個過程,進程仍然是屬於阻塞的狀態。
這就是非阻塞式IO模型
IO multiplexing就是咱們說的select,poll,epoll 。爲什麼叫多路複用,是由於它I/O多路複用能夠同時監聽多個fd,如此就減小了爲每一個須要監聽的fd開啓線程的開銷。
select調用是內核級別的,能夠等待多個socket,能實現同時對多個IO端口進行監聽,當其中任何一個socket的數據準好了,
就能返回進行可讀,
而後進程再進行recvform系統調用,將數據由內核拷貝到用戶進程,這個過程是阻塞的。
I/O複用模型會用到select、poll、epoll函數,這幾個函數也會使進程阻塞,可是和阻塞I/O所不一樣的的,這幾個函數能夠同時阻塞多個I/O操做`。並且能夠同時對多個讀操做,多個寫操做的I/O函數進行檢測,直到有數據可讀或可寫時(不是等到socket數據所有到達再處理, 而是有了一部分數據就會調用用戶進程來處理),才真正調用I/O操做函數。
IO複用有人把其成爲同步非阻塞的,也有稱爲同步阻塞。其實這個是否阻塞還須要看第一個階段,第一個階段有的阻塞,有的不阻塞。主要也是阻塞在select階段,屬於用戶主動等待階段,咱們且規範爲阻塞狀態,因此,把IO多路複用歸爲同步阻塞模式
。
這是IO複用的模型:
信號驅動式I/O:首先咱們容許Socket進行信號驅動IO,並安裝一個信號處理函數,進程繼續運行並不阻塞。當數據準備好時,進程會收到一個SIGIO信號,能夠在信號處理函數中調用I/O操做函數處理數據。
也就是說第一個階段,徹底是非阻塞的,等數據到達會給一個信號通知,第二個階段recvfrom仍是阻塞過程,和之上無差別。
信號驅動式I/O 過程以下:
異步IO不是順序執行,用戶進程進行aio_read系統調用以後,不管內核數據是否準備好,都會直接返回給用戶進程,而後用戶態進程能夠去作別的事情。等到socket數據準備好了,內核直接複製數據給進程,而後從內核向進程發送通知
。IO兩個階段,進程都是非阻塞的
。
針對這5中IO模型,我採用一張圖來總結一下。
Unix中的五種I/O模型,除信號驅動I/O外,Java對其它四種I/O模型都有所支持。其中Java最先提供的blocking I/O便是同步阻塞I/O,而NIO便是同步非阻塞I/O,同時經過NIO實現的Reactor模式便是I/O複用模型的實現,經過AIO實現的Proactor模式便是異步I/O模型的實現。
因此說嚴格意義上來講,經過Reactor模式實現的NIO,和unix中的I/O多路複用是相同的概念,但這是一種編程模型,而不是原生支持。這也是咱們下面所要進行的netty講解的主要思想。
更多架構知識,歡迎關注個人公衆號,大碼候(cool_wier)