IO 之 BIO NIO AIO
目錄
- IO 之 BIO NIO AIO
- 前言
-
- 1.1 什麼是IO
- 1.2 核心緩沖區&程序緩沖區
- 1.3 系統調用
- 1.4 同步與異步
- 1.5 阻塞與非阻塞
- 2. 五種主要的IO模型
- 3、使用步驟
-
- 3.1 同步阻塞BIO(Blocking IO)
- 3.2 同步非阻塞NIO(None Blocking IO)
- 3.3 IO多路複用模型(I/O multiplexing)
- 3.4 信号驅動IO(SIGIO)
- 3.5 異步IO模型(asynchronous IO)
- 總結
前言
在學習之前,先把基礎知識拿來鋪墊 !
1.1 什麼是IO
在計算機中指Input/Output,也就是輸入和輸出。由于程式和運作時資料是在記憶體中駐留,由CPU這個超快的計算核心來執行,涉及到資料交換的地方,通常是磁盤、網絡等。
1.2 核心緩沖區&程序緩沖區
磁盤是資料塊的集合,核心會對磁盤上的資料塊做緩沖。緩沖過程如下
- 核心将磁盤上的資料塊複制到核心緩沖區中,當一個使用者空間中的程序要從磁盤上讀資料時,核心一般不直接讀磁盤,而是将核心緩沖區中的資料複制到程序的緩沖區中。
- 當程序所要求的資料塊不在核心緩沖區時,核心會把相應的資料塊加入到請求隊列,然後把該程序挂起,接着為其 他程序服務。
- 一段時間之後(其實很短的時間),核心把相應的資料塊從磁盤讀到核心緩沖區,然後再把資料複制到程序的緩沖區中,最後喚醒被挂起的程序。
使用者緩沖區的目的是為了減少系統調用次數,進而降低作業系統在使用者态與核心态切換所耗費的時間。
1.3 系統調用
使用者程式進行IO的讀寫,基本上會用到read&write兩大系統調用。可能不同作業系統,名稱不完全一樣,但是功能是一樣的。
read系統調用,并不是把資料直接從實體裝置,讀資料到記憶體,是把資料從核心緩沖區複制到程序緩沖區 。
write系統調用,也不是直接把資料,寫入到實體裝置,是把資料從程序緩沖區複制到核心緩沖區。
這個兩個系統調用,都不負責資料在核心緩沖區和磁盤之間的交換。底層的讀寫交換,是由作業系統kernel核心完成的。
1.4 同步與異步
同步
對于同步型的調用,應用層需要自己去向系統核心問詢,如果資料還未讀取完畢,那此時讀取檔案的任務還未完成,應用層根據其阻塞和非阻塞的劃分,或挂起或去做其他事情;如果資料已經讀取完畢,那此時系統核心将資料傳回給應用層,應用層即可以用取得的資料做其他相關的事情。
異步
而對于異步型的調用,應用層無需主動向系統核心問詢,在系統核心讀取完檔案資料之後,會主動通知應用層資料已經讀取完畢,此時應用層即可以接收系統核心傳回過來的資料,再做其他事情。
比如我去銀行辦理業務,可能選擇排隊等候,也可能取一個小紙條上面有我的号碼,等到排到我這一号時由櫃台的人通知我輪到我去辦理業務了.
排隊等候就是同步等待消息,而取小紙條等待别人通知就是異步等待消息.在異步消息進行中,等待辦理業務的人往往注冊一個回調機制,在所等待的事件被觸發時由櫃台的人通過小紙條上的号碼找到等待該事件的人.
1.5 阻塞與非阻塞
阻塞
無資料準備好,系統調用比如read,recvfrom就會挂起,等到有資料準備好或者有資料了才繼續執行系統調用,最後才從系統調用的函數中傳回。
非阻塞
這裡的非阻塞是通過忙輪詢去檢測是否有資料準備好,沒有資料準備好就一直輪詢,直到有資料準備好了可以開始進行資料的複制了為止。
繼續上面的那個例子,不論是排隊還是使用号碼等待通知,如果在這個等待的過程中,等待者除了等待消息之外不能做其它的事情,那麼該機制就是阻塞的
相反,有的人喜歡在銀行辦理這些業務的時候一邊打玩手機一邊等待,這樣的狀态就是非阻塞的,因為他(等待者)沒有阻塞在這個消息通知上,而是一邊做自己的事情一邊等待
2. 五種主要的IO模型
(1)同步阻塞IO(Blocking IO)
(2)同步非阻塞IO(Non-blocking IO)
(3)IO多路複用(IO Multiplexing)
(4)信号驅動IO(SIGIO)
(5)異步IO(Asynchronous IO)
3、使用步驟
3.1 同步阻塞BIO(Blocking IO)
示例:
(1)當使用者線程調用了read系統調用,核心(kernel)就開始了IO的第一個階段:準備資料。很多時候,資料在一開始還沒有到達(比如,還沒有收到一個完整的Socket資料包),這個時候kernel就要等待足夠的資料到來。
(2)當kernel一直等到資料準備好了,它就會将資料從kernel核心緩沖區,拷貝到使用者緩沖區(使用者記憶體),然後kernel傳回結果。
(3)從開始IO讀的read系統調用開始,使用者線程就進入阻塞狀态。一直到kernel傳回結果後,使用者線程才解除block的狀态,重新運作起來。
Blocking IO的特點是:在核心進行IO執行的兩個階段,使用者線程都被block了。
BIO的優點:
- 程式簡單。
- 在阻塞等待資料期間,使用者線程挂起。
- 使用者線程基本不會占用 CPU 資源。
BIO的缺點:
一般情況下,會為每個連接配接配套一條獨立的線程,或者說一條線程維護一個連接配接成功的IO流的讀寫。在并發量小的情況下,這個沒有什麼問題。但是,當在高并發的場景下,需要大量的線程來維護大量的網絡連接配接,記憶體、線程切換開銷會非常巨大。是以,基本上,BIO模型在高并發場景下是不可用的。
3.2 同步非阻塞NIO(None Blocking IO)
(1)在核心資料沒有準備好的階段,使用者線程發起IO請求時,立即傳回。使用者線程需要不斷地發起IO系統調用。
(2)核心資料到達後,使用者線程發起系統調用,使用者線程阻塞。核心開始複制資料。它就會将資料從kernel核心緩沖區,拷貝到使用者緩沖區(使用者記憶體),然後kernel傳回結果。
(3)使用者線程才解除block的狀态,重新運作起來。經過多次的嘗試,使用者線程終于真正讀取到資料,繼續執行。
NIO的優點:
每次發起的 IO 系統調用,在核心的等待資料過程中可以立即傳回。使用者線程不會阻塞,實時性較好。
NIO的缺點:
需要不斷的重複發起IO系統調用,這種不斷的輪詢,将會不斷地詢問核心,這将占用大量的 CPU 時間,系統資源使用率較低。
總之,NIO模型在高并發場景下,也是不可用的。一般 Web 伺服器不使用這種 IO 模型。一般很少直接使用這種模型,而是在其他IO模型中使用非阻塞IO這一特性。java的實際開發中,也不會涉及這種IO模型。
再次說明,Java NIO(New IO) 不是IO模型中的NIO模型,而是另外的一種模型,叫做IO多路複用模型( IO multiplexing )。
3.3 IO多路複用模型(I/O multiplexing)
select,poll,epoll都是IO多路複用的機制。所謂I/O多路複用機制,就是說通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程式進行相應的讀寫操作。但select,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒後自己負責進行讀寫,也就是說這個讀寫過程是阻塞的
select模式(時間複雜度O(n)):
在Linux中有一段這樣的描述:
select() and pselect() allow a program to monitor multiple file descriptors, waiting until one or more of the file descriptors become “ready” for some class of I/O operation (e.g., input possible). A file descriptor is considered ready if it is possible to perform the corresponding I/O operation (e.g., read(2)) without blocking.
大緻意思如下:
select()和 pselect()允許程式監視多個檔案描述符,直到一個或多個檔案描述符“準備好”進行某類I/O操作(例如,可能的輸入)。(例如,如果描述符沒有被阻塞,它就可以執行讀操作)。
大緻過程如下:
(1)進行select系統調用,查詢可以讀的連接配接。kernel會查詢所有select的可查詢socket清單(fds),一旦某個描述符就緒,select就會傳回。
(2)拿到檔案描述符集合後,進行周遊(在使用者空間處理),擷取到就緒的檔案描述符,發起read()系統調用,此時的調用都是有效的,即所有調用的檔案描述符處于就緒狀态。
poll模式(時間複雜度O(n)):
poll本質上和select沒有差別,它将使用者傳入的數組拷貝到核心空間,然後查詢每個fd對應的裝置狀态, 但是它沒有最大連接配接數的限制,原因是它是基于連結清單來存儲的。
優點:
跟NIO相比,就減少了不斷輪詢的成本,減少了CPU的壓力。
缺點:
select系統調用需要将檔案描述符在使用者空間和核心空間進行傳輸,使用者線程需自行判斷那些檔案描述符處于就緒狀态。屬于同步IO
epoll模式(時間複雜度O(1)):
Linux 系統中對epoll有這樣一段描述:
The epoll API performs a similar task to poll(2): monitoring multiple file descriptors to see if I/O is possible on any of them.
大緻意思是:
epoll API 執行與poll() 類似的任務:監視多個檔案描述符,以确定它們中的任何一個是否可以進行I/O。
同時,此模式涉及到記憶體位址映射(mmap),簡而言之就是将使用者空間的一段記憶體區域映射到核心空間,映射成功後,使用者對這段記憶體區域的修改可以直接反映到核心空間,同樣,核心空間對這段區域的修改也直接反映使用者空間。
大緻過程如下:
1.使用者空間和核心空間會建立一個記憶體位址映射,将所有需要觸發io的檔案描述符維護在核心空間可以直接通路的記憶體中。
2.當觸發epoll函數後,核心空間會傳回已經就緒的檔案描述符所在的記憶體位址映射位置,使用者空間通過映射關系擷取到就緒的檔案描述符。
3.使用者空間線程發起read()/write()調用,将就緒的檔案描述符作為參數傳遞。
優點:
跟select模式相比,通過共享空間,減少了檔案描述符的拷貝和周遊的過程。
缺點:
屬于同步IO,不論select 或者 epoll 都需要進行read()/write()系統調用,此時使用者線程處于阻塞狀态
epoll跟select都能提供多路I/O複用的解決方案。在現在的Linux核心裡有都能夠支援,其中epoll是Linux所特有,而select則應該是POSIX所規定,一般作業系統均有實作,Java的NIO(new IO)技術,使用的就是IO多路複用模型。在linux系統上,使用的是epoll系統調用。
3.4 信号驅動IO(SIGIO)
應用程序使用 sigaction 系統調用,核心立即傳回,應用程序可以繼續執行,也就是說等待資料階段應用程序是非阻塞的。核心在資料到達時向應用程序發送 SIGIO 信号,應用程序收到之後在信号處理程式中調用 recvfrom 将資料從核心複制到應用程序中。信号驅動 I/O 的 CPU 使用率很高。
3.5 異步IO模型(asynchronous IO)
應用程序執行 aio_read 系統調用會立即傳回,應用程序可以繼續執行,不會被阻塞,核心會在所有操作完成之後向應用程序發送信号。
總結
本文以向面試官吹牛逼為己任,這些内容面試前還是要看看的!!
如有問題,歡迎留言讨論!!!!