node中異步IO的理解


解釋性語言和編譯型語言的區別:

計算器不能直接的理解高級語言,只能理解機器語言,所以必須把高級語言翻譯為機器語言,翻譯的方式有兩種,一個是編譯,一個是解釋。

解釋性語言的程序不需要編譯,它是在運行程序的時候進行翻譯,比如java,專門有一個解釋器可以直接執行Java程序,每一個語句都是執行的時候才能翻譯,編譯型就是編譯的時候直接編譯成機器可以執行的,編譯和執行時分開的,但是不能跨平台。因為翻譯只做了一次,運行的時候不需要再去翻譯,所以編譯型語言的程序執行效率高。

對於解釋性語言,程序運行時的控制權在解釋器而不在於程序,對於編譯型語言程序運行時的控制權在程序。

進程的前台運行和后台運行

后台進程是一直運行的服務端程序,又稱為守護進程,通常是在系統后台運行,沒有控制終端,不與前台交互,一般作為系統服務使用。其稱為后台進程的原因大部分是因為它沒有控制端,無法和前台的用戶交互。

相對應的前台進程,就是我們在終端中開啟的進程,例如我們在終端中npm run server.js啟動一個webServer,此時啟動的進程就是前台進程,當你把當前的命令行終端進行關閉了之后,該進程也便被殺死了。

node中的阻塞/非阻塞IO和同步/異步IO

表面上來看,這兩組的概念都差不多,阻塞/非阻塞IO,是操作系統內核對於IO的兩種處理方式,對於阻塞IO,比如讀取文件,操作系統在讀取完文件之后,才會給應用程序返回結果,這一段過程呢,應用程序在等待操作系統的回復,是為應用層面的同步IO。

對於非阻塞IO,操作系統在接收到應用程序對於讀取文件的請求時,立即返回給應用程序一個結果,但是應用程序怎么知道操作系統完成了IO操作呢?這時候應用程序就會對操作系統發起詢問(你到底好了沒有?人家都快急死了),發起詢問的方法又經過好幾種演變,比如read、poll、epoll等,中間多的無非就是根據文件描述符減少詢問的次數,總體上來說這種方式不好。並不能達到我們理想的異步IO。

那么從應用程序方面來將,我們期望的異步IO,就是應用程序進行了IO操作之后,不再需要操心操作系統什么時候返回,去執行下邊的代碼就行了,當操作執行完了之后呢,直接給應用程序發信息告訴他就行了。Linux系統下原生提供了一種AIO是通過信號或者回調來傳遞數據的,這個AIO就是我們的理想的異步IO。但是不幸的是只有Linux中有,而且無法利用系統緩存。

node(單線程)中對於*nix平台而言,采用的是線程池+epoll異步IO模擬實現應用程序層面的異步IO,主線程進行執行程序,碰到我們異步IO調用時,將改異步IO分配給線程池中的某一個線程,然后就變成了線程池中的某個線程和操作系統的阻塞IO進行IO操作,當IO線程接收到操作系統的阻塞IO執行的返回結果之后,IO線程再發送時間給主線程。

node中對於window平台而言,是依靠於IOCP來實現的,其內部仍然是線程池原理,不同之處在於這些線程池由系統內核接手管理。

node中對於異步IO的實現:

對於異步IO的實現,其中有幾個組成部分:事件循環、觀察者、請求對象

事件循環是node中的一種執行機制,這種機制是回調執行的基礎部分,它保證了我們的回調函數能夠被執行。

觀察者是暴露回調函數的窗口,如果整體的場景為飲料工廠的話,我們的瓶子就是我們的回調函數,事件循環就是傳送帶在那一直轉,而觀察者就是瓶子就如機器的入口,機器就是我們的應用程序。所以應用程序從觀察者這里獲取事件,應用程序詢問觀察者是否還有事件。

請求對象,是應用程序封裝的一個對象,里邊包含了要做的IO操作類型,以及回調函數。

整體流程就是,異步調用開始之后,應用程序封裝一個請求對象,送入我們的線程池中的某個線程,該線程和操作系統的非阻塞IO通過epoll機制進行工作,這其中,會有觀察者在線程池中進行檢查,當某個線程的IO操作完成之后,觀察者會將回調函數(封裝在請求對象中的)放在事件循環上(上段提到的傳送帶),然后主線程調用回調函數。

參考:《深入淺出Node.js》
問題思考:

  • 操作系統的線程與CPU中的線程有什么不同?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM