天天看點

玩轉BIO,NIO,AIO模型, 阻塞 同步 與IO多路複用

1.啥是IO?

2.講明白同步和阻塞倆概念

3.啥是IO多路複用,作業系統層的演進

4.對比BIO,NIO,AIO

四步玩轉IO模型

1.啥是IO

計算機核心三大功能:  Input  計算  OutPut

IO重要吧 沒有IO計算機變成玩具,

那IO又可分為幾種 常用的,磁盤IO  網絡IO  

閉眼睛想 如果IO不給力,計算機運算能力再牛逼 會不會影響整體效率的,非常影響

從底層磁盤看IO:  啥是磁盤IO

2. 同步與阻塞

同步與異步,指的是API,接口,服務調用等層面的概念。

同步指的是一步一步來,上一步執行完 傳回結果 才能執行下一步, 異步指的是 不需要等上一步執行完傳回結果,就能執行下一步。

舉個簡單的例子就是 java線程池, 把具體的任務丢到線程池裡就不管了,主線程可以繼續幹别的事了,不需要阻塞在那等任務執行完,線程池主要的目的也是為了完成異步。

通常異步的執行可以通過回調接口來擷取執行結果,如FutureTask,可以擷取到異步執行的情況

阻塞與非阻塞,指的是底層作業系統IO層面的概念。

阻塞指的是 程序調用作業系統執行IO,IO執行完成之前,程序會一直阻塞在那等待,直到IO完成擷取到傳回結果

非阻塞指的是 程序調用作業系統執行IO,IO沒有完成會直接傳回失敗結果,程序循環去調用直到IO完成,或者IO完成後作業系統回調接口來通知程序拿資料。總而言之 程序不需要在那一動不動的阻塞住等IO  這就是非阻塞。

現在差別開同步和阻塞,一個是應用接口調用層面,一個是底層作業系統IO層面,兩者的思想很相近

模拟一個場景:

我們這台機器作為服務端,另一台機器作為用戶端

用戶端請求建立一次網絡連接配接,随後通過網絡傳輸資料

此時在我們機器讀取資料過程

玩轉BIO,NIO,AIO模型, 阻塞 同步 與IO多路複用

我們建立一個線程,用來監聽新建立的Socket,讀取發來的資料

阻塞式IO:

線程調用read函數,如果沒有資料發來 就一直阻塞,

資料從網卡傳到核心态,核心緩沖區複制到使用者緩沖區,傳輸過程中線程保持阻塞狀态, 直到拿到資料傳回結果

非阻塞IO:

不用等資料傳輸完,即可傳回給線程一個結果(-1 或失敗), 這樣可以放開線程去幹别的事,

需要使用者線程循環調用 read,直到傳回值不為 -1,再開始處理業務。

但是這樣有個明顯的缺點    線程想拿到資料 就得不斷一遍一遍調用,一遍一遍詢問資料傳好了沒有, 也會有不小的開銷,

多線程并發執行的情況下 每一個Socke監聽都會耗着一個線程,消耗線程資源

IO多路複用 select:

不再為每一次Socket監聽建立一個線程了,而是一個線程同時服務多個Socket套接字

玩轉BIO,NIO,AIO模型, 阻塞 同步 與IO多路複用

在使用者态,每建立一個套接字 添加一個檔案描述符fd 到數組裡

使用者态将fd數組拷貝到核心态,執行select函數

select函數 讓核心去周遊數組中的每個元素,執行非阻塞的判斷,判斷是否Socket準備好資料了

最後select傳回準備好的fd個數,通知使用者态

使用者态執行read函數去讀資料

優點:

由核心态做周遊,有效的減少了每次周遊判斷要經過使用者态 核心态之間切換 !!

問題:

1.select傳回結果隻是fd個數,具體哪個fd準備好了還要使用者态自己周遊 !!

2.select需要拷貝fd數組到核心态,高并發場景下消耗較大

3.select在周遊的過程中也是阻塞的,使用者态需要阻塞的等待核心态傳回結果

IO多路複用 poll:

poll隻是在select基礎上增強,它和 select 的主要差別就是,去掉了 select 隻能監聽 1024 個檔案描述符的限制。

IO多路複用 epoll:

1. 核心中儲存一份檔案描述符集合,無需使用者每次都重新傳入,隻需告訴核心修改的部分即可。

2. 核心不再通過輪詢的方式找到就緒的檔案描述符,而是通過異步 IO 事件喚醒。

3. 核心僅會将有 IO 事件的檔案描述符傳回給使用者,使用者也無需周遊整個檔案描述符集合。

總結一下 整個過程,

IO多路複用解決的問題是,我的服務端同時和十個用戶端建立連接配接 等着用戶端發資料,那我怎麼随時随地直到哪個用戶端發資料了 我好讀資料呢?

原始辦法就是每一個連接配接 建立一個線程阻塞的等待用戶端消息,這顯然不夠友好

IO多路複用就通過一個線程同時監聽十個用戶端,輪訓着用戶端套接字 哪個好了通知我讀哪個不就好了。

如果輪訓周遊是在使用者态完成的,每一次判斷資料準備好都要使用者态切核心态,核心态切使用者态 這樣是不是效率很低?

索性把是個用戶端代表的檔案描述符傳到核心态,讓核心态去周遊,周遊出結果再告訴使用者态就完了呗。

最後的read讀資料 還是要使用者态去讀。隻不過是有确定的去讀

4.對比BIO,NIO與AIO

BIO: 也稱同步阻塞IO,顧名思義 應用調用層是同步的,作業系統層是阻塞的。 可以參考java InputStream流,當read磁盤資料時,read()方法會阻塞在那,而底層也會阻塞在那等待IO完成 

NIO: 也稱同步非阻塞IO,應用調用層是同步的,作業系統層是非阻塞的。 如果基于NIO進行網絡通信,采取的就是多路複用的IO模型,這個多路複用IO模型針對的是網絡通信中的IO場景來說的。

AIO: 也成異步非阻塞IO,也叫做NIO2.0 異步IO模型,應用調用層是異步的,作業系統層是非阻塞的。應用層調用不管IO是否成功都會直接傳回,不會阻塞住線程。你需要提供一個回調函數給AIO接口,一旦底層系統核心完成了具體的IO請求,比如網絡讀寫之類的,就會回調你提供的回調函數。