作者:黃湘龍
IO在計算機世界中地位舉足輕重,IO效率一直是碼農(nóng)們孜孜不倦最求的目標丧失。本文我們一起來研究下Linux的IO的工作方式是如何一步步進化到今天的。我們說的IO主要是指應用程序在工作過程中用到的IO類型,包括兩種IO:文件IO和網(wǎng)絡IO,本文主要研究的是網(wǎng)絡IO斋攀。應用進程和內(nèi)核之間的數(shù)據(jù)交互方式一直在演進,下面我們對各種形態(tài)的交互方式進行介紹礁芦。在這之前蜻韭,我們先明確幾個概念:內(nèi)核空間和用戶空間、同步和異步柿扣、阻塞和非阻塞。
- 內(nèi)核空間
操作系統(tǒng)單獨擁有的內(nèi)存空間為內(nèi)核空間闺魏,這塊內(nèi)存空間獨立于其他的應用內(nèi)存空間未状,除了操作系統(tǒng),其他應用程序不允許訪問這塊空間析桥。但操作系統(tǒng)可以同時操作內(nèi)核空間和用戶空間司草。
- 用戶空間
單獨給用戶應用進程分配的內(nèi)存空間,操作系統(tǒng)和應用程序都可以訪問這塊內(nèi)存空間泡仗。
- 同步
調(diào)用線程發(fā)出同步請求后埋虹,在沒有得到結(jié)果前,該調(diào)用就不會返回娩怎。所有同步調(diào)用都必須是串行的搔课,前面的同步調(diào)用處理完了后才能處理下一個同步調(diào)用。
- 異步
調(diào)用線程發(fā)出異步請求后截亦,在沒有得到結(jié)果前爬泥,該調(diào)用就返回了。真正的結(jié)果數(shù)據(jù)會在業(yè)務處理完成后通過發(fā)送信號或者回調(diào)的形式通知調(diào)用者崩瓤。
- 阻塞
調(diào)用線程發(fā)出請求后袍啡,在沒有得到結(jié)果前,該線程就會被掛起却桶,此時CPU也不會給此線程分配時間境输,此線程處于非可執(zhí)行狀態(tài)。直到返回結(jié)果返回后,此線程才會被喚醒嗅剖,繼續(xù)運行辩越。劃重點:線程進入阻塞狀態(tài)不占用CPU資源。
- 非阻塞
調(diào)用線程發(fā)出請求后窗悯,在沒有得到結(jié)果前区匣,該調(diào)用就返回了,整個過程調(diào)用線程不會被掛起蒋院。
1. 同步阻塞IO
同步阻塞IO模式是Linux中最常用的IO模型亏钩,所有Socket通信默認使用同步阻塞IO模型。同步阻塞IO模型中欺旧,應用線程在調(diào)用了內(nèi)核的IO接口后姑丑,會一直被阻塞,直到內(nèi)核將數(shù)據(jù)準備好辞友,并且復制到應用線程的用戶空間內(nèi)存中栅哀。常見的Java的BIO,阻塞模式的Socket網(wǎng)絡通信就是使用這種模式進行網(wǎng)絡數(shù)據(jù)傳輸?shù)摹?/p>
如圖所示称龙,當應用線程發(fā)起讀操作的IO請求時留拾,內(nèi)核收到請求后進入等待數(shù)據(jù)階段,此時應用線程會處于阻塞的狀態(tài)鲫尊。當內(nèi)核準備好數(shù)據(jù)后痴柔,內(nèi)核會將數(shù)據(jù)從內(nèi)核空間拷貝到用戶內(nèi)存空間,然后內(nèi)核給應用線程返回結(jié)果疫向,此時應用線程才解除阻塞的狀態(tài)咳蔚。
同步阻塞模式的特點是內(nèi)核 IO 在執(zhí)行等待數(shù)據(jù)讀取到內(nèi)核空間和將數(shù)據(jù)復制到用戶空間的兩個階段,應用線程都被阻塞搔驼。
同步阻塞模式簡單直接谈火,沒有下面幾種模式的線程切換、回調(diào)舌涨、通知等消耗糯耍,在并發(fā)量較少的網(wǎng)絡通信場景下是最好的選擇。
在大規(guī)模網(wǎng)絡通信的場景下泼菌,大量的請求和連接需要處理谍肤,線程被阻塞是不可接受的。雖然當前的網(wǎng)絡 I/O 有一些解決辦法哗伯,如使用一個線程來處理一個客戶客戶端的連接荒揣,出現(xiàn)阻塞時只是一個線程阻塞而不會影響其它線程工作,還有為了減少系統(tǒng)線程的開銷焊刹,采用線程池的辦法來減少線程創(chuàng)建和回收的成本系任,但是有一些使用場景同步阻塞模式仍然是無法解決的恳蹲。如當前一些需要大量 HTTP 長連接的情況,像淘寶現(xiàn)在使用的 Web 旺旺項目俩滥,服務端需要同時保持幾百萬的 HTTP 連接嘉蕾,但是并不是每時每刻這些連接都在傳輸數(shù)據(jù),這種情況下不可能同時創(chuàng)建這么多線程來保持連接霜旧。這種情況错忱,我們想給某些客戶端更高的服務優(yōu)先級,很難通過設計線程的優(yōu)先級來完成挂据,我們需要另外一種新的 I/O 操作模式以清。
優(yōu)點:
并發(fā)量較少的網(wǎng)絡通信場景較高效
應用程序開發(fā)簡單
缺點:
不適合并發(fā)量較大的網(wǎng)絡通信場景
2. 同步非阻塞IO
同步非阻塞IO是同步阻塞IO的一種變種IO模式,它和同步阻塞區(qū)別在于崎逃,應用線程在向內(nèi)核發(fā)送IO請求后掷倔,內(nèi)核的IO數(shù)據(jù)在沒有準備好的時候會立刻給應用線程返回一個錯誤代碼(EAGAIN 或 EWOULDBLOCK),在內(nèi)核的IO數(shù)據(jù)準備好了之后个绍,應用線程再發(fā)起IO操作請求時候勒葱,內(nèi)核會在將IO數(shù)據(jù)從內(nèi)核空間復制到用戶空間后給應用線程返回正常應答。常見的Non-Blocking模式的Socket網(wǎng)絡通信就是同步非阻塞模式巴柿。
如圖所示凛虽,當用戶線程發(fā)起讀操作時,如果內(nèi)核的IO數(shù)據(jù)還沒有準備好广恢,那么它不會阻塞掉用戶線程涩维,而是會直接返回一個 EAGAIN/EWOULDBLOCK 錯誤。從用戶線程的角度袁波,它發(fā)起一個讀操作后立即就得到了一個結(jié)果,用戶進程判斷結(jié)果是 EAGAIN/EWOULDBLOCK 之后會再次發(fā)起讀操作蜗侈。這種利用返回值不斷調(diào)用被稱為輪詢(polling)篷牌,顯而易見,這么做會耗費大量 CPU 時間踏幻。一旦內(nèi)核中的IO數(shù)據(jù)準備好了枷颊,并且又再次收到了用戶進程的請求,那么它馬上就將數(shù)據(jù)拷貝到了用戶內(nèi)存该面,然后返回夭苗。
優(yōu)點:
在內(nèi)核IO數(shù)據(jù)準備階段不會阻塞應用線程,適合對線程阻塞敏感的網(wǎng)絡應用
缺點:
輪詢查詢內(nèi)核IO數(shù)據(jù)狀態(tài)隔缀,耗費大量CPU题造,效率低
需要不斷輪序,增加開發(fā)難度
3. 多路復用
多路復用是目前大型互聯(lián)網(wǎng)應用中最常見的一種IO模型猾瘸,簡單說就是應用進程中有一個IO狀態(tài)管理器界赔,多個網(wǎng)絡IO注冊到這個管理器上丢习,管理器使用一個線程調(diào)用內(nèi)核API來監(jiān)聽所有注冊的網(wǎng)絡IO的狀態(tài)變化情況,一旦某個連接的網(wǎng)絡IO狀態(tài)發(fā)生變化淮悼,能夠通知應用程序進行相應的讀寫操作咐低。多路網(wǎng)絡IO復用這個狀態(tài)管理器,所以叫多路復用模式袜腥。多路復用本質(zhì)上是同步阻塞见擦,但與傳統(tǒng)的同步阻塞多線程模型相比,IO 多路復用的最大優(yōu)勢是在處理IO高并發(fā)場景時只使用一個線程就完成了大量的網(wǎng)絡IO狀態(tài)的管理工作羹令,系統(tǒng)資源開銷小鲤屡。Java的NIO,Nginx都是用的多路復用模式進行網(wǎng)絡傳輸特恬。多路復用的基本工作流程:
- 應用程序?qū)⒕W(wǎng)絡IO注冊到狀態(tài)管理器;
- 狀態(tài)管理器通過調(diào)用內(nèi)核API來確認所管理的網(wǎng)絡IO的狀態(tài)执俩;
- 狀態(tài)管理器探知到網(wǎng)絡IO的狀態(tài)發(fā)生變化后,通知應用程序進行實質(zhì)的同步阻塞讀寫操作癌刽。
目前Linux主要有三種狀態(tài)管理器:select役首,poll,epoll显拜。epoll是Linux目前大規(guī)模網(wǎng)絡并發(fā)程序開發(fā)的首選模型衡奥,在絕大多數(shù)情況下性能遠超select和poll。目前流行的高性能Web服務器Nginx正式依賴于epoll提供的高效網(wǎng)絡套接字輪詢服務远荠。但是矮固,在并發(fā)連接不高的情況下,多線程+阻塞I/O方式可能性能更好譬淳。他們也是不同歷史時期的產(chǎn)物:
- select出現(xiàn)是1984年在BSD里面實現(xiàn)的档址;
- 14年之后也就是1997年才實現(xiàn)了poll,其實拖那么久也不是效率問題邻梆, 而是那個時代的硬件實在太弱守伸,一臺服務器處理1千多個鏈接簡直就是神一樣的存在了,select很長段時間已經(jīng)滿足需求浦妄;
- 2002, 大神 Davide Libenzi 實現(xiàn)了epoll尼摹;
這三種狀態(tài)管理器都是通過不同的內(nèi)核API來監(jiān)視網(wǎng)絡連接的狀態(tài),不同的API提供不同的能力剂娄,導致了性能上的差異蠢涝,下面我們逐個分析下。
3.1 select
select是最古老的多路復用模型阅懦,Linux在2.6版本之前僅提供select模式和二,一度是主流的網(wǎng)絡IO模式。select采取定期輪詢的方式將自己管理的所有網(wǎng)絡IO對應的文件句柄發(fā)送給內(nèi)核故黑,進行狀態(tài)查詢儿咱,下面是內(nèi)核系統(tǒng)對應用程序提供的API:
int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout);
fd_set是一個Long的數(shù)組的數(shù)據(jù)結(jié)構(gòu)庭砍,用于存放的是文件句柄(file descriptor)。這個API有三個關(guān)鍵參數(shù)混埠,即readset/writeset/exceptset怠缸,前兩個參數(shù)是注冊到select,所有需要監(jiān)聽的網(wǎng)絡IO文件句柄數(shù)組钳宪,第三個參數(shù)是一個空數(shù)組揭北,由內(nèi)核輪詢所有網(wǎng)絡IO文件句柄后,將狀態(tài)有變化的文件句柄值寫入到exceptset數(shù)組中吏颖,也就是說readset/writeset是輸入數(shù)據(jù)搔体,exceptset是輸出數(shù)據(jù)。最后半醉,內(nèi)核將變化的句柄數(shù)數(shù)量返回給調(diào)用者疚俱。
從接口的細節(jié),我們可以看到歸納下select的工作流程:
- 應用線程將需要監(jiān)視的網(wǎng)絡IO文件句柄注冊到select狀態(tài)監(jiān)視器缩多;
- select狀態(tài)監(jiān)視器工作線程定期調(diào)用內(nèi)核API呆奕,將自己所有管理的文件句柄通過(readset/writeset)兩個參數(shù)傳給內(nèi)核;
- 內(nèi)核輪詢所有傳進來的文件句柄的網(wǎng)絡IO狀態(tài)衬吆,將有變化的文件句柄值寫入exceptset數(shù)組中梁钾,并且將變化的句柄數(shù)數(shù)量返回給調(diào)用者;
- select工作線程通知應用程序進行實質(zhì)的同步阻塞讀寫操作逊抡。
select機制的特性分析:
- 每次調(diào)用select姆泻,都需要把readset/writeset集合從用戶空間態(tài)拷貝到內(nèi)核空間,如果readset/writeset集合很大時冒嫡,那這個開銷很大拇勃;
- 每次調(diào)用select都需要在內(nèi)核遍歷傳遞進來的所有文件句柄,每次調(diào)用都進行線性遍歷孝凌,時間復雜度為O(n)潜秋,文件句柄集合很大時,那這個開銷也很大胎许;
- 內(nèi)核對被監(jiān)控的文件句柄集合大小做了限制,X86為1024罗售,X64為2048辜窑。
3.2 poll
poll模型和select模型非常類似,狀態(tài)監(jiān)視器同樣管理一批網(wǎng)絡IO狀態(tài)寨躁,內(nèi)核同樣對傳輸過來的所有網(wǎng)絡IO文件句柄進行線性輪詢來確認狀態(tài)穆碎,唯一區(qū)別是應用線程傳輸給內(nèi)核的文件句柄數(shù)組不限制大小,解決了select中說道的第三個問題职恳,其他兩個問題依然存在所禀。
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
typedef struct pollfd {
int fd; // 需要被檢測或選擇的文件描述符
short events; // 對文件描述符fd上感興趣的事件
short revents; // 文件描述符fd上當前實際發(fā)生的事件
} pollfd_t;
這個是內(nèi)核提供的poll的API方面,fds是一個struct pollfd類型的數(shù)組,用于存放需要檢測其狀態(tài)的網(wǎng)絡IO文件句柄色徘,并且調(diào)用poll函數(shù)之后fds數(shù)組不會被清空恭金;pollfd結(jié)構(gòu)體表示一個被監(jiān)視的文件描述符。其中褂策,結(jié)構(gòu)體的events域是監(jiān)視該文件描述符的事件掩碼横腿,由用戶來設置這個字段,結(jié)構(gòu)體的revents字段是文件句柄的操作結(jié)果事件掩碼斤寂,內(nèi)核在調(diào)用返回時設置這個字段耿焊。
3.3 epoll
epoll在Linux2.6內(nèi)核正式提出,是基于事件驅(qū)動的I/O方式遍搞,相對于select來說罗侯,epoll沒有文件句柄個數(shù)限制,將應用程序關(guān)心的網(wǎng)絡IO文件句柄的事件存放到內(nèi)核的一個事件表中溪猿,在用戶空間和內(nèi)核空間的copy只需一次钩杰。epoll內(nèi)核和網(wǎng)絡設備建立了訂閱回調(diào)機制,一旦注冊到內(nèi)核事件表中的網(wǎng)絡連接狀態(tài)發(fā)生了變化再愈,內(nèi)核會收到網(wǎng)絡設備的通知榜苫,訂閱回調(diào)機制替換了select/poll的輪詢查詢機制,將時間復雜度從原來的O(n)降低為O(1)翎冲,大幅提升IO效率垂睬,特別是在大量并發(fā)連接中只有少量活躍的場景。
Linux提供的三個epoll的API:
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
epoll將select/poll的一個大API變成了三個API抗悍,目的就是先把需要監(jiān)聽的句柄數(shù)據(jù)通過epoll_ctl接口在內(nèi)核注冊了驹饺,不用每次查詢網(wǎng)絡IO狀態(tài)的時候傳一大堆數(shù)據(jù)過去。epoll在內(nèi)核內(nèi)存里建了一個紅黑樹用于存儲epoll_ctl傳來的連接缴渊,epoll內(nèi)核還會建立一個rdllist雙向鏈表赏壹,用于存儲網(wǎng)絡狀態(tài)發(fā)生變化的文件句柄,當epoll_wait調(diào)用時衔沼,僅僅觀察這個rdllist雙向鏈表里有沒有數(shù)據(jù)即可蝌借,有數(shù)據(jù)就返回,沒有數(shù)據(jù)就讓epoll_wait睡眠指蚁,等到timeout時間到后即使鏈表沒數(shù)據(jù)也返回摹量。因為epoll不像select/poll那樣采取輪詢每個連接來確認狀態(tài)的方法锚赤,而是監(jiān)聽一個雙向鏈表沛膳,在連接數(shù)很多的情況下官辽,epoll_wait非常高效。
所有添加到epoll中的網(wǎng)絡連接都會與設備(如網(wǎng)卡)驅(qū)動程序建立回調(diào)關(guān)系搓劫,也就是說相應連接狀態(tài)的發(fā)生時網(wǎng)絡設備會調(diào)用回調(diào)方法通知內(nèi)核瞧哟,這個回調(diào)方法在內(nèi)核中叫做ep_poll_callback混巧,它會把網(wǎng)絡狀態(tài)發(fā)生變化的變更事件放到上面的rdllist雙向鏈表中。
當調(diào)用epoll_wait檢查是否有狀態(tài)變更事件的連接時勤揩,只是檢查eventpoll對象中的rdllist雙向鏈表是否有數(shù)據(jù)而已咧党,如果rdllist鏈表不為空,則將鏈表中的事件復制到用戶態(tài)內(nèi)存(使用MMAP提高效率)中雄可,同時將事件數(shù)量返回給用戶凿傅。epoll_ctl在向epoll對象中添加、修改数苫、刪除監(jiān)聽的網(wǎng)絡IO文件句柄時聪舒,從rbr紅黑樹中查找事件也非常快虐急,也就是說epoll是非常高效的箱残,它可以輕易地處理百萬級別的并發(fā)連接。
epoll的epoll_wait調(diào)用止吁,有EPOLLLT和EPOLLET兩種觸發(fā)返回模式被辑,LT是默認的模式,更加安全敬惦,ET是“高速”模式盼理,更加高效:
- 水平觸發(fā)(LT):默認工作模式,即當epoll_wait檢測到某網(wǎng)絡連接狀態(tài)發(fā)生變化并通知應用程序時俄删,應用程序可以不立即處理該事件宏怔;下次調(diào)用epoll_wait時,會再次通知此事件畴椰;
- 邊緣觸發(fā)(ET): 當epoll_wait檢測到某網(wǎng)絡連接狀態(tài)發(fā)生變化并通知應用程序時臊诊,應用程序必須立即處理該事件。如果不處理斜脂,下次調(diào)用epoll_wait時抓艳,不會再次通知此事件。
epoll機制的特性分析:
- 沒有最大并發(fā)連接的限制帚戳,能打開的FD的上限遠大于1024(1G的內(nèi)存上能監(jiān)聽超過10萬個連接)玷或;
- 通過epoll_ctl方法將網(wǎng)絡連接注冊到內(nèi)核,不用每次查詢連接狀態(tài)時將所有網(wǎng)絡文件句柄傳輸傳輸給內(nèi)核片任,大幅提高效率庐椒;
- 內(nèi)核和網(wǎng)絡設備建立事件訂閱機制,監(jiān)聽連接網(wǎng)絡狀態(tài)不使用輪詢的方式蚂踊,不會隨著文件句柄數(shù)目的增加效率下降,只有活躍可用的文件句柄才會觸發(fā)回調(diào)函數(shù)笔宿;Epoll最大的優(yōu)點就在于它只管你“活躍”的連接犁钟,而跟連接總數(shù)無關(guān)棱诱;
- 利用MMAP內(nèi)存映射技術(shù)加速用戶空間與內(nèi)核空間的消息傳遞,減少復制開銷涝动。
目前大部分主流的應用都是基于此種IO模型構(gòu)建的迈勋,比如Nginx,NodeJS醋粟,Netty框架等靡菇,總結(jié)一下多路復用的特點:
優(yōu)點:
使用一個線程監(jiān)控多個網(wǎng)絡連接狀態(tài),性能好米愿,特別是進化最終形態(tài)epoll模式厦凤,適合大量連接業(yè)務場景
缺點:
較復雜,應用開發(fā)難度大
4 信號驅(qū)動
信號驅(qū)動模式利用linux信號機制育苟,通過sigaction函數(shù)將sigio讀寫信號以及handler回調(diào)函數(shù)注冊到內(nèi)核隊列中较鼓,注冊后應用進程不堵塞,可以去干別的工作违柏。當網(wǎng)絡IO狀態(tài)發(fā)生變化時觸發(fā)SIGIO中斷博烂,通過調(diào)用應用程序的handler通知應用程序網(wǎng)絡IO就緒了。信號驅(qū)動的前半部分操作是異步行為漱竖,后面的網(wǎng)絡數(shù)據(jù)操作仍然屬于同步阻塞行為禽篱。
優(yōu)點:
這種異步回調(diào)方式避免用戶或內(nèi)核主動輪詢設備造成的資源浪費
缺點:
handler是在中斷環(huán)境下運行,多線程不穩(wěn)定馍惹,而且平臺兼容性不好躺率,不是一個完善可靠的解決方案,實際應用場景少
較復雜讼积,開發(fā)難度大
5. 異步IO
異步IO通過一些列異步API實現(xiàn)肥照,是五種IO模式中唯一個真正的異步模式,目前Java的AIO使用的就是本模式勤众。異步模式的讀操作通過調(diào)用內(nèi)核的aio_read函數(shù)來實現(xiàn)舆绎。應用線程調(diào)用aio_read,遞交給內(nèi)核一個用戶空間下的緩沖區(qū)们颜。內(nèi)核收到請求后立刻返回吕朵,不阻塞應用線程。當網(wǎng)絡設備的數(shù)據(jù)到來后窥突,內(nèi)核會自動把數(shù)據(jù)從內(nèi)核空間拷貝到aio_read函數(shù)遞交的用戶態(tài)緩存努溃。拷貝完成后以信號的方式通知用戶線程阻问,用戶線程拿到數(shù)據(jù)后就可以執(zhí)行后續(xù)操作梧税。
異步IO模式與信號驅(qū)動IO的區(qū)別在于:信號驅(qū)動IO由內(nèi)核通知應用程序什么時候可以開始IO操作,異步IO則由內(nèi)核告訴應用程序IO操作何時完成。異步IO主動把數(shù)據(jù)拷貝到用戶空間第队,不需要調(diào)用recvfrom方法把數(shù)據(jù)從內(nèi)核空間拉取到用戶態(tài)空間哮塞。異步IO是一種推數(shù)據(jù)的機制,相比于信號處理IO拉數(shù)據(jù)的機制效率會更高凳谦。
異步IO還是屬于比較新的IO模式忆畅,需要操作系統(tǒng)支持,Linux2.5版本首次提供異步IO尸执,Linux2.6及以后版本家凯,異步IO的API都屬于標準提供。異步IO目前沒有太多的應用場景如失。
優(yōu)點:
純異步绊诲,高效率高性能。
缺點:
效率比多路復用模式?jīng)]有質(zhì)的提升岖常,成熟應用遷移模式的動力不足驯镊,一直沒有大規(guī)模成熟應用來支撐
較復雜,開發(fā)難度大
6. 總結(jié)
五種Linux的IO模式各有特色竭鞍,存在即合理板惑,各有自己的應用場景。目前偎快,大家在寫一些簡單的低并發(fā)Socket通信時大多數(shù)還是使用多線程加同步阻塞的方式冯乘,效率和其他模式差不多,實現(xiàn)起來會簡單很多晒夹。
目前市面上流行的高并發(fā)網(wǎng)絡通信框架裆馒,Nginx、基于Java的NIO的Netty框架和NodeJS等都是使用使用的多路復用模型丐怯,經(jīng)過大量實際項目驗證喷好,多路復是目前最成熟的高并發(fā)網(wǎng)絡通信IO模型。多路復用模型中的epoll是最優(yōu)秀的读跷,目前Linux2.6以上的系統(tǒng)提供標準的epoll的API梗搅,Java的NIO在Linux2.6及以上版本都會默認提供epoll的實現(xiàn),在低于Linux2.6版本上會提供poll的實現(xiàn)效览。Windows目前還不支持epoll无切,只支持select,不過也什么丐枉,基本上沒什么人用Windows來做網(wǎng)絡應用服務器哆键。
信號驅(qū)動IO感覺不太成熟,基本上沒有見過使用場景瘦锹。純異步模式籍嘹,內(nèi)核把所有事情做了闪盔,看起來很美好,Java也提供了響應的實現(xiàn)辱士,但由于效率比多路復用模式?jīng)]有質(zhì)的提升锭沟,成熟應用遷移模式的動力不足,一直沒有大規(guī)模成熟應用來支撐识补。
參考:
《使用異步 I/O 大大提高應用程序的性能》
https://www.ibm.com/developerworks/cn/linux/l-async/index.html
《深入分析 Java I/O 的工作機制》
https://www.ibm.com/developerworks/cn/java/j-lo-javaio/index.html
《IO多路復用的三種機制Select,Poll辫红,Epoll》
http://www.reibang.com/p/397449cadc9a