事件驅動編程是一種編程範式,這裏程序的執行流由外部事件來決定。它的特色是包含一個事件循環,當外部事件發生時使用回調機制來觸發相應的處理。另外兩種常見的編程範式是(單線程)同步以及多線程編程。 html
當咱們面對以下的環境時,事件驅動模型一般是一個好的選擇: linux
網絡應用程序一般都有上述這些特色,這使得它們可以很好的契合事件驅動編程模型。 web
具體介紹見:IO多路複用(番外篇) 編程
討論的背景是Linux環境下的network IO 緩存
IO多路複用(epool)和gevent模塊(協程)區別與聯繫: 安全
都是遇到IO就切換,在linux下底層都是經過libevent.so實現。能夠認爲gevent是對IO多路複用更上層的封裝,IO多路複用是其默認設置,其更專一於任務之間的切換。網絡
操做系統的核心是內核,能夠訪問受保護的內存空間,也有訪問底層硬件設備的全部權限。爲了保證內核的安全,用戶進程不能直接操做內核(kernel),系統將虛擬空間劃分爲兩部分,一部分爲內核空間,一部分爲用戶空間。 多線程
爲了控制進程的執行,內核必須有能力掛起正在CPU上運行的進程,並恢復之前掛起的某個進程的執行。這種行爲被稱爲進程切換。 併發
正在執行的進程,因爲期待的某些事件未發生,如請求系統資源失敗、等待某種操做的完成、新數據還沒有到達或無新工做作等,則由系統自動執行阻塞原語(Block),使本身由運行狀態變爲阻塞狀態。可見,進程的阻塞是進程自身的一種主動行爲,也所以只有處於運行態的進程(得到CPU),纔可能將其轉爲阻塞狀態。當進程進入阻塞狀態,是不佔用CPU資源的。 app
文件描述符(File descriptor)是計算機科學中的一個術語,是一個用於表述指向文件的引用的抽象化概念。文件描述符在形式上是一個非負整數。實際上,它是一個索引值,指向內核爲每個進程所維護的該進程打開文件的記錄表。(文件句柄是真實的文件對象)
注:文件描述符這一律念每每只適用於UNIX、Linux這樣的操做系統。
又被稱做標準 I/O,大多數文件系統的默認 I/O 操做都是緩存 I/O。在 Linux 的緩存 I/O 機制中,操做系統會將 I/O 的數據緩存在文件系統的頁緩存( page cache )中,也就是說,數據會先被拷貝到操做系統內核的緩衝區中,而後纔會從操做系統內核的緩衝區拷貝到應用程序的地址空間。
缺點:
數據在傳輸過程當中須要在應用程序地址空間和內核進行屢次數據拷貝操做,這些數據拷貝操做所帶來的 CPU 以及內存開銷是很是大的。
對於一次IO訪問(以read舉例),數據會先被拷貝到操做系統內核的緩衝區中,而後纔會從操做系統內核的緩衝區拷貝到應用程序的地址空間。(內核態到用戶態數據拷貝)
因此,當一個read操做發生時,它會經歷兩個階段:
1. 等待數據準備 (Waiting for the data to be ready)
2. 將數據從內核拷貝到進程中 (Copying the data from the kernel to the process)
正是由於這兩個階段,linux系統產生了下面五種網絡模式的方案。
阻塞IO(bloking IO)
linux下,默認狀況下全部的socket都是blocking。其特色就是在IO執行的兩個階段都是阻塞的 。
非阻塞 I/O(nonblocking IO)
linux下,能夠經過設置socket使其變爲non-blocking。其特色是IO執行的第一個階段不阻塞,用戶進程不斷的主動詢問kernel數據有沒有準備好,kernel作出相應的迴應,但IO執行的第二個階段仍然是阻塞的。
I/O 多路複用( IO multiplexing)
就是咱們說的select,poll,epoll,也稱這種IO方式爲event driven IO。select/epoll的好處就在於單個process就能夠同時處理多個網絡鏈接的IO。它的基本原理就是select,poll,epoll這個function會不斷的輪詢所負責的全部socket,當某個socket有數據到達了,就通知用戶進程數據準備好了,但IO執行的第二個階段仍然是阻塞的。
這個圖和blocking IO的圖其實並無太大的不一樣,事實上,其性能還更差一些。由於這裏須要使用兩個system call (select 和 recvfrom),而blocking IO只調用了一個system call (recvfrom)。可是,用select的優點在於它能夠同時處理多個connection。
因此,若是處理的鏈接數不是很高的話,使用select/epoll的web server不必定比使用multi-threading + blocking IO的web server性能更好,可能延遲還更大。select/epoll的優點並非對於單個鏈接能處理得更快,而是在於能處理更多的鏈接。
在IO multiplexing Model中,實際中,對於每個socket,一般都設置成爲non-blocking,可是,如上圖所示,整個用戶的process實際上是一直被block的。只不過process是被select這個函數block,而不是被socket IO給block。
異步 I/O(asynchronous IO)
linux下的asynchronous IO其實用得不多。
用戶進程發起read操做以後,就能夠開始去作其它的事。從kernel的角度,當它收到一個asynchronous read以後,首先它會馬上返回信號給用戶進程(確認收到請求的信號?),因此不會對用戶進程產生任何block。而後,kernel等待數據準備完成後,將數據拷貝到用戶內存,拷貝完成以後,kernel會給用戶進程發送一個signal,告訴它read操做完成了。
各個IO Model的比較
具體介紹見:Python Select 解析
select目前幾乎在全部的平臺上支持,其良好跨平臺支持也是它的一個優勢,可是使用select單個進程可以監視的文件描述符的數量存在最大限制,在Linux上通常爲1024,不過能夠經過修改宏定義甚至從新編譯內核的方式提高這一限制。select監視到文件描述符有活躍,只向用戶進程返回有活躍信號,沒有返回具體活躍的文件描述符,用戶進程還需再次循環檢查浪費時間和資源。
poll和select在本質上沒有多大差異,可是poll沒有最大文件描述符數量的限制,能夠看做是一個過渡階段。
epoll直到Linux2.6纔出現,它幾乎具有了以前所說的一切優勢,被公認爲Linux2.6下性能最好的多路I/O就緒通知方法。epoll能夠同時支持水平觸發和邊緣觸發(Edge Triggered,只告訴進程哪些文件描述符剛剛變爲就緒狀態,它只說一遍,若是咱們沒有采起行動,那麼它將不會再次告知,這種方式稱爲邊緣觸發),理論上邊緣觸發的性能要更高一些,可是代碼實現至關複雜。
注:epoll在linux上的文件描述符數量,可能會受到OS用戶最大鏈接數限制,注意調整。
服務端
客戶端
封裝好的IO多路複用模塊,默認使用epool,當系統不支持時使用select。
服務端
多併發客戶端
Twisted本身用異步形式重寫了SSH、DNS、FTP、HTTP等,代碼比較複雜,遊戲開發會用到。
http://www.cnblogs.com/alex3714/articles/5248247.html
參考:
http://www.cnblogs.com/alex3714/articles/5248247.html
http://www.cnblogs.com/alex3714/articles/5876749.html
http://www.cnblogs.com/alex3714/p/4372426.html