我所理解的IO基礎:IO、NIO、AIO的一些基本概念

1,名詞解釋linux

IO:Input/Output表示數據的交換,廣義上的IO包括文件IO、網絡IO、其餘IO。本篇只討論網絡IO,也就是socket IO。segmentfault

IO:默認表示傳統的Blocking IO(阻塞式IO)。
NIO:JDK1.4推出,Sun官方叫作NewIO,民間叫作Non-blocking IO(非阻塞IO)。
AIO:JDK1.7推出,Asynchronous IO(異步IO),也叫NIO2.0。安全

2,爲何分爲這幾種IO?網絡

JVM虛擬機運行在操做系統上,JVM的native方法至關因而對操做系統API的封裝(抽象)。因此,首先得理解操做系統支持的IO。
Linux操做系統支持的網絡IO有5種形式:異步

  • 阻塞 I/O(blocking IO)
  • 非阻塞 I/O(nonblocking IO)
  • I/O 多路複用( IO multiplexing)
  • 信號驅動 I/O( signal driven IO)
  • 異步 I/O(asynchronous IO)

因爲signal driven IO在實際中並不經常使用,只介紹其餘4種IO Model。介紹以前,得須要瞭解一些概念。socket

2.1,網絡數據是如何從網卡內存轉移到用戶線程中的?async

用戶線程發送讀取命令到操做系統,操做系統將數據從網卡中讀取到內核空間中,再從內核空間轉移到用戶線程中。函數

2.2,什麼是內核空間,什麼是用戶線程空間?操作系統

爲了保證用戶進程不能直接操做內核(kernel),保證內核的安全,操心繫統將虛擬空間劃分爲兩部分,一部分爲內核空間,一部分爲用戶空間。針對32位的linux操做系統而言,將最高的1G字節(從虛擬地址0xC0000000到0xFFFFFFFF),供內核使用,稱爲內核空間,而將較低的3G字節(從虛擬地址0x00000000到0xBFFFFFFF),供各個進程使用,稱爲用戶空間。線程

一句話總結:爲了安全操做,網卡中的數據須要先讀到內核空間中,再轉移到用戶空間中。

最重要的是要理解:分爲兩步操做來完成一個讀的操做

2.3,什麼是線程阻塞?

用戶線程發起請求,告訴操做系統,須要讀取數據。操做系統去讀取數據放到內核空間中,再從內核空間寫到用戶線程中,這都是須要時間的。用戶線程只能等待數據就緒以後才能執行,那這段時間內總不能佔着CPU使用權不放吧?因而,用戶線程阻塞等待獲取到數據(沒法繼續執行後續代碼),會主動釋放掉CPU執行權。

2.4.1,什麼是阻塞 I/O(blocking IO)

在linux中,默認狀況下全部的socket都是blocking,一個典型的讀操做流程大概是這樣:

圖片來源於網絡

當用戶進程調用了recvfrom這個系統調用,kernel就開始了IO的第一個階段:將數據從網卡讀到內核中。因爲網絡的接收是須要時間的(網絡用戶的數據並無徹底傳輸完),從網卡讀到內核也是須要時間的,因此第一階段阻塞。當kernel一直等到數據準備好了,它就會將數據從kernel中拷貝到用戶內存,而後kernel返回結果,用戶進程才解除block的狀態,從新運行起來。

blocking IO的特色就是在IO執行的兩個階段時,用戶線程都處於阻塞狀態。

2.4.2,什麼是非阻塞 I/O(non-blocking IO)

linux下,能夠經過設置socket使其變爲non-blocking。當對一個non-blocking socket執行讀操做時,流程是這個樣子:

圖片來源於網絡

當用戶進程發出read操做時,若是kernel中的數據尚未準備好,那麼它並不會block用戶進程,而是馬上返回一個error。從用戶進程角度講 ,它發起一個read操做後,並不須要等待,而是立刻就獲得了一個結果。用戶進程判斷結果是一個error時,它就知道數據尚未準備好,因而它能夠再次發送read操做。一旦kernel中的數據準備好了,而且又再次收到了用戶進程的system call,那麼它立刻就將數據拷貝到了用戶內存,而後返回。

nonblocking IO的特色是用戶進程須要不斷的主動詢問kernel數據好了沒有。

2.4.3,什麼是I/O 多路複用( IO multiplexing) 在non-blocking IO中,用戶線程不斷的主動詢問一個數據有沒有準備好,比較浪費cpu。因而,由一個線程來主動輪詢多個數據是否準備好,每次均可以返回多個標識符,表示哪些數據內核空間已經準備好了,用戶能夠讀取了。

圖片來源於網絡

當用戶進程調用了select,那麼整個進程會被block,而同時,kernel會「監視」全部select負責的socket,當任何一個socket中的數據準備好了,select就會返回。這個時候用戶進程再調用read操做,將數據從kernel拷貝到用戶進程。

I/O 多路複用的特色是經過一種機制一個進程能同時等待多個文件描述符,而這些文件描述符(套接字描述符)其中的任意一個進入讀就緒狀態,select()函數就能夠返回。

用select的優點在於它能夠同時處理多個connection。

2.4.4,什麼是異步 I/O(asynchronous IO)

inux下的asynchronous IO其實用得不多。先看一下它的流程:

圖片來源於網絡

用戶進程發起read操做以後,馬上就能夠開始去作其它的事。而另外一方面,從kernel的角度,當它受到一個asynchronous read以後,首先它會馬上返回,因此不會對用戶進程產生任何block。而後,kernel會等待數據準備完成,而後將數據拷貝到用戶內存,當這一切都完成以後,kernel會給用戶進程發送一個signal,告訴它read操做完成了。

2.5,幾種IO的比較

各個IO Model的比較如圖所示: 圖片來源於網絡

2.6,同步IO和異步IO的區別

在說明synchronous IO和asynchronous IO的區別以前,須要先給出二者的定義。POSIX的定義是這樣子的:

  • 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;

這裏的「I/O operation」包括上文提到的兩步:1,內核空間數據的準備;2,將數據從內核空間拷貝到用戶空間。

同步IO和異步IO二者的區別就在於同步IO作」IO operation」的時候會將線程阻塞。按照這個定義,以前所述的blocking IO,non-blocking IO,IO multiplexing都屬於synchronous IO。只有最後一種asynchronous I/O是異步IO。

=======================================分割線==================================

舉個例子說明。

假設你有個朋友A,託你幫他在花店定作一束花,再幫他送給B。

blocking IO:你在花店裏一直等待花作好,而後親自送給B,等待B簽收。這期間你不能幹別的事情。

non-blocking IO:你不停給花店打電話詢問花是否作好了,確認作好了以後你去拿花而且親自送給B,等待B簽收。

每次打電話詢問A的一束花,效率過低。因而你又開始幫朋友A1,A2...An在花店定作花而且送給B1,B2...Bn。

這樣每打一次電話,都有可能收到許多花準備好的通知。(不必定有前後順序,畢竟定作的花也有難易程度)

IO multiplexing:每次打電話給花店能夠詢問許多花是否準備好,花店給你的回覆是其中某些花準備好了。因而你把這些已經準備好的花拿到而且一個一個的親自送給目標B,等待B簽收後送下一個目標B。

每次都是你親自送花,這樣效率過低了。某天花店推出了一項業務,能夠幫助顧客送花,這樣就不用你親自送了。

asynchronous IO:你告訴花店定作的花,而且告訴花店這束花送到目標B的地址,讓花店去送給B。而後你隨意幹什麼都行。等花店把花送達B的時候會給你發個通知。

朋友A就是客戶端,目標B就是用戶線程,你就是Java的IO底層程序。你僱傭的人就是線程池。

參考資料:
1,Linux IO模式及 select、poll、epoll詳解
2,IO模型解惑

相關文章
相關標籤/搜索