一篇文章讀懂Reactor和Proactor兩種I/O多路複用模式

FeelTouch發表於2018-02-06

前言

兩種I/O多路複用模式:Reactor和Proactor。
一般地,I/O多路複用機制都依賴於一個事件多路分離器(Event Demultiplexer)。分離器物件可將來自事件源的I/O事件分離出來,並分發到對應的read/write事件處理器(Event Handler)。開發人員預先註冊需要處理的事件及其事件處理器(或回撥函式);事件分離器負責將請求事件傳遞給事件處理器。兩個與事件分離器有關的模式是Reactor和Proactor。Reactor模式採用同步IO,而Proactor採用非同步IO。
在Reactor中,事件分離器負責等待檔案描述符或socket為讀寫操作準備就緒,然後將就緒事件傳遞給對應的處理器,最後由處理器負責完成實際的讀寫工作。
在Proactor模式中,處理器--或者兼任處理器的事件分離器,只負責發起非同步讀寫操作。IO操作本身由作業系統來完成。傳遞給作業系統的引數需要包括使用者定義的資料緩衝區地址和資料大小,作業系統才能從中得到寫出操作所需資料,或寫入從socket讀到的資料。事件分離器捕獲IO操作完成事件,然後將事件傳遞給對應處理器。比如,在windows上,處理器發起一個非同步IO操作,再由事件分離器等待IOCompletion事件。典型的非同步模式實現,都建立在作業系統支援非同步API的基礎之上,我們將這種實現稱為“系統級”非同步或“真”非同步,因為應用程式完全依賴作業系統執行真正的IO工作。

Reactor-同步IO實現

Reactor模式:主執行緒(IO處理單元)只負責監聽socket檔案描述符上是否有事件發生,有的話就立即將該事件通知工作執行緒(邏輯處理單元)。除此之外,主執行緒不做任何其他實質性的工作,讀寫資料、接受新的連線請求、處理客戶端請求均在工作執行緒中完成。 
  以epoo_wait()為例實現的Reactor模式的工作流程為(所有IO複用技術都是基於同步IO模型): 
  (1)主執行緒往epoll核心事件表中註冊socket的可讀事件(就緒讀),進而呼叫epoll_wait()監聽socket上的可讀事件。
  (2)當socket上有資料可讀時(含有客戶端來連線),epoll_wait()通知主執行緒,主執行緒將socket的可讀事件放入請求佇列。 
  (3)喚醒在請求佇列上的某個工作執行緒(請求佇列上有多個工作執行緒,空閒時是休眠狀態),工作執行緒執行相應操作後往該socket上寫入請求的處理結果。
 主執行緒中的epoll_wait()只是用於檢測監聽socket上的連線而不能用來檢測連線socket上的讀寫事件,因為讀寫事件是主程式和工作執行緒通過aio_read()和aio_write()來向核心註冊的,核心負責監聽讀寫事件且執行讀寫操作,讀寫完畢後再通過訊號機制嚮應用程式報告,應用程式的訊號處理函式完成後續操作。

Proactor-非同步IO實現

Proactor模式是將所有IO事件操作都交由主執行緒和核心處理,工作執行緒只負責業務邏輯。 
  使用非同步IO模式實現的Proactor模式的工作流程:(以aio_read()和aio_write()為例) 
  (1)主執行緒呼叫aio_read()函式向核心註冊sockst上的讀完成事件,並告訴核心在使用者空間的讀緩衝區的位置,以及讀操作完成時如何通知應用程式(比如訊號機制)。 
  (2)主執行緒繼續處理業務邏輯。 
  (3)當socket上的資料被讀入使用者緩衝區後(讀操作交由核心完成),核心將嚮應用程式傳送訊號以通知應用程式資料可用。 
  (4)應用程式在預先定義的訊號處理函式中選擇一個工作執行緒來處理客戶需求。工作執行緒處理完客戶請求後呼叫aio_write()函式向核心註冊socket上的寫完成事件,並告訴核心使用者寫緩衝區的位置以及寫操作完成時如何通知應用程式,即寫操作交由核心執行。 
  (5)主執行緒繼續處理其他業務邏輯。 
  (6)當使用者緩衝區的資料被核心寫入socket後核心嚮應用程式傳送一個訊號以通知應用程式傳送完畢。 
  (7)應用程式預先定義好的訊號處理函式選擇一個工作執行緒來做善後處理,如決定是否關閉socket連線。 
 主執行緒中的epoll_wait()只是用於檢測監聽socket上的連線而不能用來檢測連線socket上的讀寫事件,因為讀寫事件是主程式和工作執行緒通過aio_read()和aio_write()來向核心註冊的,核心負責監聽讀寫事件且執行讀寫操作,讀寫完畢後再通過訊號機制嚮應用程式報告,應用程式的訊號處理函式完成後續操作。

Proactor-同步IO模擬

Proactor模式的讀寫socket操作是交由核心處理,即非同步IO模式,但是程式設計使用的IO複用技術是同步IO,我們可以利用同步IO方式模擬出Proactor模式,其原理為:主執行緒執行資料的讀寫操作(和監聽listen_socket),讀寫完成後主執行緒向工作通知這一”完成事件”,那麼從工作執行緒的角度看,和非同步IO實現的Proactor模式的工作執行緒一樣,它們直接獲得了資料的讀寫結果,接下來要做的只是對讀寫結果進行邏輯處理。 
  以epoll_wait()為例,使用同步IO模型模擬Proactor模式的工作流程: 
  (1)主執行緒往epoll核心事件表中註冊socket上的讀就緒事件,並呼叫epoll_wait()等待socket上有資料可讀(有客戶端連線也是可讀) 
  (2)當socket上有資料可讀時epoll_wait()將通知主執行緒,主執行緒從socket迴圈讀取資料直到沒有更多資料可讀,然後將讀取到的資料封裝成一個請求物件並插入請求佇列。主執行緒呼叫epoll_wait()等待socket可寫,以返回資料給該socket。 
  (3)請求佇列上某個睡眠的工作執行緒被喚醒,它獲得請求物件並處理該請求,然後往epoll核心事件表中註冊socket上的寫就緒事件(目的是要返回資料給該socket)。 
  (4)當socket可寫時,epoll_wait()通知主執行緒,主執行緒往socket上寫入服務端處理客戶端的請求結果,即工作執行緒的資料。 

 參考:http://blog.csdn.net/qq_29344757/article/details/79054623

相關文章