Linux操作系統(tǒng)的I/O模型
JAVA的NIO引入了異步I/O,而Node.js宣稱的就是異步編程,I/O自然是異步的。其實操作系統(tǒng)在很早就引入了異步I/O的概念,如下圖(摘自Unix網(wǎng)絡(luò)編程中的圖片):

我對上圖的理解有幾點(diǎn):
- 從IO設(shè)備讀取數(shù)據(jù)到用戶內(nèi)存的整個過程都是由系統(tǒng)內(nèi)核來完成;
- 數(shù)據(jù)總是先被拷貝到內(nèi)核緩沖區(qū),再由內(nèi)核緩沖區(qū)拷貝到用戶內(nèi)存;
- 除了異步I/O,其余4種I/O模型其實都是阻塞的,至少在數(shù)據(jù)從內(nèi)核拷貝到用戶內(nèi)存時是阻塞的;
- 雖然異步I/O看上去是理想解決方案,但實現(xiàn)上現(xiàn)在用得最多的應(yīng)該是多路I/O復(fù)用,有select、poll、epoll的實現(xiàn),性能最好的是epoll;
- 異步I/O現(xiàn)在被認(rèn)為有缺陷,僅支持O_DIRECT而無法支持系統(tǒng)緩存。
Node.js中的異步I/O
因為內(nèi)核中的異步I/O有缺陷,現(xiàn)實中的異步I/O通常由用戶態(tài)的線程池模擬完成,如下圖:

Node.js中原本使用了libeio異步I/O庫,在v0.9.3后改為自己實現(xiàn)的線程池來完成異步I/O。所以在Node.js中,除了用戶的Javascript代碼是單線程外,所有I/O都是多線程并行執(zhí)行的。
Node.js中的異步I/O調(diào)用
Node.js通過事件循環(huán)的模式運(yùn)行,在每一個循環(huán)的過程中,通過詢問一個或多個觀察者來判斷是否有事件要處理,而觀察者可以有文件I/O觀察者、網(wǎng)絡(luò)I/O觀察者等。
Node.js中異步I/O調(diào)用的大致流程如下:
- 發(fā)起I/O調(diào)用
- 用戶通過Javascript代碼調(diào)用Node核心模塊,將參數(shù)和回調(diào)函數(shù)傳入到核心模塊;
- Node核心模塊會將傳入的參數(shù)和回調(diào)函數(shù)封裝成一個請求對象;
- 將這個請求對象推入到I/O線程池等待執(zhí)行;
- Javascript發(fā)起的異步調(diào)用結(jié)束,Javascript線程繼續(xù)執(zhí)行后續(xù)操作。
- 執(zhí)行回調(diào)
- I/O操作完成后,會將結(jié)果儲存到請求對象的result屬性上,并發(fā)出操作完成的通知;
- 每次事件循環(huán)時會檢查是否有完成的I/O操作,如果有就將請求對象加入到I/O觀察者隊列中,之后當(dāng)做事件處理;
- 處理I/O觀察者事件時,會取出之前封裝在請求對象中的回調(diào)函數(shù),執(zhí)行這個回調(diào)函數(shù),并將result當(dāng)參數(shù),以完成Javascript回調(diào)的目的。
微信訂閱號:
源文地址:http://blog.gopersist.com/2015/03/09/aio/