1 IO 模型之IO多路複用併發
解決NIO的思路就是降解無效的系統調用,如何降解呢?咱們一塊兒來看看如下幾種IO多路複用的解決思路。異步
1.1. IO多路複用之select/pollide
Select是內核提供的系統調用,它支持一次查詢多個系統調用的可用狀態,當任意一個結果狀態可用時就會返回,用戶進程再發起一次系統調用進行數據讀取。換句話說,就是NIO中N次的系統調用,藉助Select,只須要發起一次系統調用就夠了。其IO流程以下所示:高併發
可是,select有一個限制,就是存在鏈接數限制,針對於此,又提出了poll。其與select相比,主要是解決了鏈接限制。性能
select/epoll 雖然解決了NIO重複無效系統調用用的問題,但同時又引入了新的問題。問題是:優化
用戶空間和內核空間之間,大量的數據拷貝spa
內核循環遍歷IO狀態,浪費CPU時間3d
換句話說,select/poll雖然減小了用戶進程的發起的系統調用,但內核的工做量只增不減。在高併發的狀況下,內核的性能問題依舊。因此select/poll的問題本質是:內核存在無效的循環遍歷。orm
1.2. IO多路複用之epollblog
針對select/pool引入的問題,咱們把解決問題的思路轉回到內核上,如何減小內核重複無效的循環遍歷呢?變主動爲被動,基於事件驅動來實現。其流程圖以下所示:
epoll相較於select/poll,多了兩次系統調用,其中epoll_create創建與內核的鏈接,epoll_ctl註冊事件,epoll_wait阻塞用戶進程,等待IO事件。
epoll,已經大大優化了IO的執行效率,但在IO執行的第一階段:數據準備階段都仍是被阻塞的。因此這是一個能夠繼續優化的點。
2 IO 模型之信號驅動IO(SIGIO)
信號驅動IO與BIO和NIO最大的區別就在於,在IO執行的數據準備階段,不會阻塞用戶進程。以下圖所示:當用戶進程須要等待數據的時候,會向內核發送一個信號,告訴內核我要什麼數據,而後用戶進程就繼續作別的事情去了,而當內核中的數據準備好以後,內核立馬發給用戶進程一個信號,說」數據準備好了,快來查收「,用戶進程收到信號以後,立馬調用recvfrom,去查收數據。
乍一看,信號驅動式I/O模型有種異步操做的感受,可是在IO執行的第二階段,也就是將數據從內核空間複製到用戶空間這個階段,用戶進程仍是被阻塞的。
綜上,你會發現,無論是BIO仍是NIO仍是SIGIO,它們最終都會被阻塞在IO執行的第二階段。那若是能將IO執行的第二階段變成非阻塞,那就完美了。
3 IO 模型之異步IO(AIO)
異步IO真正實現了IO全流程的非阻塞。用戶進程發出系統調用後當即返回,內核等待數據準備完成,而後將數據拷貝到用戶進程緩衝區,而後發送信號告訴用戶進程IO操做執行完畢(與SIGIO相比,一個是發送信號告訴用戶進程數據準備完畢,一個是IO執行完畢)。其流程以下:
因此,之因此稱爲異步IO,取決於IO執行的第二階段是否阻塞。所以前面講的BIO,NIO和SIGIO均爲同步IO。