在上一篇博文中我們提到異步請求是從上層開始,一層一層轉發到最下面的服務層的對象win_iocp_socket_service,由它將請求轉發到操作系統(調用windows api),操作系統處理完異步請求之後又是如何返回給應用程序的呢,這裡是通過iocp(完成端口)來實現的。讓我們先來簡要的看看iocp的基本步驟:
asio實際上也是按照這個步驟去做的,再回頭看看上一節中的那個簡單的例子:
asio::io_service io_service; tcp::socket socket(io_service); boost::asio::async_connect(socket, server_address, connect_handler); io_service.run();
第一行中的io_service對象是asio的核心,它其實封裝了iocp,創建一個io_service實際上就是創建了一個iocp對象win_iocp_io_service,因此後面所有的io object的創建都要引用這個io_service,目的是共用這個iocp對象。第二行創建了socket對象,它引用了第一行創建的iocp對象;第三行實際上是將異步請求層層轉發到最下面的服務層win_iocp_socket_service對象,最終交給操作系統。通過它的名字就知道它與iocp相關,因為發起異步操作之前,它先要將io object對象與完成端口綁定,以便後面的完成事件會發到指定的完成端口。
綁定io object和iocp對象的具體過程是這樣的:async_connect內部會先調用base_xxx模板層的base_socket<tcp>的open方法,base_socket<tcp>又會調用服務層的服務對象stream_socket_service<tcp>的open方法,stream_socket_service<tcp>又調用最下面的服務對象win_iocp_socket_service的open方法,win_iocp_socket_service對象又委托io object對象引用的io_service對象(實際上是win_iocp_io_service)的do_open方法,在do_open方法中會調用register_handler方法,在該方法中會調用CreateIoCompletionPort將io object和iocp對象綁定起來。
io object和iocp對象綁定之後,win_iocp_socket_service會調用操作系統的api,發起異步操作。
再看第四行:io_service.run();
io_service::run()又是委托win_iocp_io_service::run()來實現的,讓我們來看看run的內部實現:
size_t win_iocp_io_service::run(boost::system::error_code& ec) { if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) { stop(); ec = boost::system::error_code(); return 0; } win_iocp_thread_info this_thread; thread_call_stack::context ctx(this, this_thread); size_t n = 0; while (do_one(true, ec)) if (n != (std::numeric_limits<size_t>::max)()) ++n; return n; }
run()首先檢查是否有需要處理的操作,如果沒有,函數退出;win_iocp_io_service使用outstanding_work_來記錄當前需要處理的任務數。如果該數值不為0,則委托do_one函數繼續處理。do_one()內部會調用GetQueuedCompletionStatus()函數,該函數會阻塞等待異步事件的完成,當異步事件完成時,就回調到應用層的完成事件處理函數,因為發起異步操作時已經將io object和完成端口綁定了,所以iocp能將異步完成事件回調到對應的應用層的完成處理函數中。
至此,asio中一個異步操作的過程就完成了。在了解了這些內部實現細節之後,我們再來看看boost官網上給出的一個asio中proactor模式的一張圖。
這張圖和上一篇博文中Proactor模式的圖幾乎是一樣的,我們根據這張圖再結合前面的分析,就能從細節中還原出asio中的Proactor模式了。下面我們來看看上圖中的這些對象分別是asio中的哪些對象:
從上面的分析可以看到,asoi中的Proactor模式已經很清晰了,io_service在asio中處於核心地位,不僅僅是對應了一個完成端口對象,還參與了Proactor模式中的異步事件處理和啟動事件循環,調度異步事件多路分離器將異步事件回調到應用層。
再來做一個小結:io object負責發起異步操作,發起異步操作的過程中,會委托stream_socket_service將異步操作轉發到下面的服務層,最終轉發到操作系統。io object創建時需要引用io_service,以便在後面綁定完成端口,同時還要提供完成事件處理函數,以便在異步操作完成後處理完成事件。io_service負責啟動事件循環,等待異步事件的完成並將異步操作的結果回發到用戶定義的完成事件處理函數中。
[1] [2] [3] http://blog.csdn.net/henan_lujun/article/details/8965044
如果你覺得這篇文章對你有用,可以點一下推薦,謝謝。
c++11 boost技術交流群:296561497,歡迎大家來交流技術。