3 回答

TA貢獻1846條經(jīng)驗 獲得超7個贊
看起來所討論的一些實體(例如:libev等)已經(jīng)失去了相關性,因為它已經(jīng)有一段時間了,但我認為這個問題仍然具有很大的潛力。
讓我嘗試在抽象的UNIX環(huán)境中,在Node的上下文中,借助抽象示例來解釋事件驅動模型的工作。
計劃的觀點:
腳本引擎開始執(zhí)行腳本。
每當遇到CPU綁定操作時,它都會在內(nèi)聯(lián)(真實機器)中完成。
每當遇到I / O綁定操作時,請求及其完成處理程序都會在“事件機器”(虛擬機)中注冊
以相同的方式重復操作,直到腳本結束。CPU綁定操作 - 執(zhí)行內(nèi)聯(lián),I / O綁定操作,如上所述向機器請求。
當I / O完成時,將回調(diào)一下監(jiān)聽器。
上面的事件機制稱為libuv AKA事件循環(huán)框架。Node利用此庫來實現(xiàn)其事件驅動的編程模型。
Node的觀點:
有一個線程來托管運行時。
拿起用戶腳本。
將其編譯為本機[杠桿v8]
加載二進制文件,然后跳轉到入口點。
已編譯的代碼使用編程原語在線執(zhí)行CPU綁定活動。
許多I / O和計時器相關的代碼都有本機包裝。例如,網(wǎng)絡I / O.
因此,I / O調(diào)用從腳本路由到C ++橋接器,I / O句柄和完成處理程序作為參數(shù)傳遞。
本機代碼執(zhí)行l(wèi)ibuv循環(huán)。它獲取循環(huán),將表示I / O的低級事件排入隊列,并將本機回調(diào)包裝器排入libuv循環(huán)結構。
本機代碼返回到腳本 - 此刻不會發(fā)生I / O!
上面的項目重復多次,直到執(zhí)行所有非I / O代碼,并且所有I / O代碼都被注冊到libuv。
最后,當系統(tǒng)中沒有任何內(nèi)容要執(zhí)行時,節(jié)點將控制權傳遞給libuv
libuv開始行動,它獲取所有已注冊的事件,查詢操作系統(tǒng)以獲得其可操作性。
在非阻塞模式下準備好I / O的那些被拾取,執(zhí)行I / O,并且發(fā)出它們的回調(diào)。一個接一個地。
尚未準備好的那些(例如套接字讀取,另一個端點尚未寫入任何內(nèi)容)將繼續(xù)用OS進行探測,直到它們可用。
循環(huán)內(nèi)部維持一個不斷增加的計時器。當應用程序請求延遲回調(diào)(例如setTimeout)時,將利用此內(nèi)部計時器值來計算觸發(fā)回調(diào)的正確時間。
雖然大多數(shù)功能都以這種方式迎合,但文件操作的一些(異步版本)是在附加線程的幫助下執(zhí)行的,并且很好地集成到libuv中。雖然網(wǎng)絡I / O操作可以等待期望外部事件,例如另一個端點響應數(shù)據(jù)等,但文件操作需要來自節(jié)點本身的一些工作。例如,如果你打開一個文件并等待fd準備好數(shù)據(jù),它就不會發(fā)生,因為沒有人正在閱讀!同時,如果您從主線程中的內(nèi)聯(lián)文件中讀取,它可能會阻止程序中的其他活動,并且可能會產(chǎn)生可見問題,因為與cpu綁定活動相比,文件操作非常慢。因此,內(nèi)部工作線程(可通過UV_THREADPOOL_SIZE環(huán)境變量配置)用于對文件進行操作,
希望這可以幫助。

TA貢獻1873條經(jīng)驗 獲得超9個贊
ibuv簡介
該Node.js的項目開始于2009年從瀏覽器分離一個JavaScript環(huán)境。使用谷歌的V8和Marc Lehmann的libev,node.js將I / O模型 - 偶數(shù) - 與一種非常適合編程風格的語言相結合; 由于瀏覽器的形成方式。隨著node.js越來越流行,讓它在Windows上運行很重要,但libev只能在Unix上運行。Windows等效的內(nèi)核事件通知機制(如kqueue或(e)輪詢)是IOCP。libuv是一個圍繞libev或IOCP的抽象,取決于平臺,為用戶提供基于libev的API。在node-v0.9.0版本的libuv中,libev被刪除了。
還有一張用@ BusyRich描述Node.js中的事件循環(huán)的圖片
根據(jù)這個doc Node.js事件循環(huán),
下圖顯示了事件循環(huán)操作順序的簡要概述。
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
注意:每個框將被稱為事件循環(huán)的“階段”。
階段概述
定時器:此階段執(zhí)行由setTimeout()和調(diào)度的回調(diào)setInterval()。
I / O回調(diào):除了執(zhí)行幾乎所有回調(diào)之外
關閉回調(diào),由計時器安排的回調(diào),和setImmediate()??臻e,準備:僅在內(nèi)部使用。
poll:檢索新的I / O事件; 節(jié)點將在適當時阻止此處。
check:setImmediate()在這里調(diào)用回調(diào)。
關閉回調(diào):例如socket.on('close', ...)。
在事件循環(huán)的每次運行之間,Node.js檢查它是否在等待任何異步I / O或定時器,如果沒有,則檢查是否干凈。
添加回答
舉報