原文地址:http://blog.csdn.net/luansxx/article/details/7854326
要用好它,就必須先了解它,而且不能停止於表面,必須深入到內部。而了解一件事物,先要了解它的框架,再了解它的細節。了解了框架,我們就有了提綱挈領的認識。
關於 boost asio 框架結構,在其文檔中,用了這樣一張圖來描述:
簡單解釋一下:
這里由使用者(Initiator)啟動一個異步操作(Asynchronous Operation),在啟動異步的同時它要負責創建一個異步回調對象(Completion Handler),然后該異步操作被交給了異步操作執行者(Asynchronous Operation Processor),由它負責執行異步操作,並在完成后將一個完成事件插入完成事件隊列(Completion Event Queue);另一方面,前攝器(Proactor,這個詞很難准確翻譯,也有翻譯為主動器,可能借義於proactive)驅動異步事件分派器(Asynchronous Event Demultiplexer)從完成事件隊列中獲取事件,這是一個阻塞的過程,一旦獲取到完成事件,前攝器從事件上找出與該事件關聯的回調對象,並執行回調。
這是一個標准的前攝器模式,這個模式是在 ACE 網絡庫中使用的概念。關於該模式的研究很多,搜索一下 ACE Proactor 就可以找到很多資料。上面的描述也比較容易理解,唯一比較難搞懂的是異步事件分派器(Asynchronous Event Demultiplexer),好像它的存在並不起多大作用,其實它的作用大着呢,特別是在多線程中,它要保證異步完成事件的及時分派,提高多線程並發度,以及降低線程切換開銷。在 windows 完成端口的文檔中有這方面的機制介紹。
總得來說,這是一個概念性的模型,僅用這個模型來描述 boost asio,根本體現不了 boost asio 的優點。即使從使用者的角度,僅掌握這樣的模型也是不夠,boost asio 還有很多值得學習借鑒的地方。
我們需要結合這個模型來深入 boost asio 的實現框架。
boost asio 是如何實現前攝器模式的呢?我們使用 boost asio 第一步都需要創建一個 boost::asio::io_service,我們就從 io_service 開始開啟我們的探秘之旅。
io_service 類的定義很簡單,總共就三個成員變量:
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) detail::winsock_init<> init_; #elif defined(__sun) || defined(__QNX__) || defined(__hpux) || defined(_AIX) \ || defined(__osf__) detail::signal_init<> init_; #endif // The service registry. boost::asio::detail::service_registry* service_registry_; // The implementation. impl_type& impl_;
其實簡單反而意味着強大,因為這表明 boost asio 已經把功能結構划分的很清晰了。
三個成員變量中的 init_ 與結構沒有太大關系,windows 平台的 winsock_init 里面調用了 WSAStartup,linux 平台的 signal_init 里面屏蔽了 SIG_PIPE 信號。這兩個東東幾乎是在我們剛開始接觸網絡編程就遇到的知識點。
其次我們再來看看 impl_ 成員,從名字上看應該是 io_service 的實現類,確實 io_service 的很多接口是直接轉發給了 impl_ 成員,比如 run、poll、stop、reset、post、dispatch。
inline std::size_t io_service::run(boost::system::error_code& ec) { return impl_.run(ec); } inline std::size_t io_service::poll(boost::system::error_code& ec) { return impl_.poll(ec); } inline void io_service::stop() { impl_.stop(); } inline void io_service::reset() { impl_.reset(); } template <typename Handler> inline void io_service::dispatch(Handler handler) { impl_.dispatch(handler); } template <typename Handler> inline void io_service::post(Handler handler) { impl_.post(handler); }
但是 impl_type 是什么呢?它的定義如下:
#if defined(BOOST_ASIO_HAS_IOCP) typedef detail::win_iocp_io_service impl_type; #elif defined(BOOST_ASIO_HAS_EPOLL) typedef detail::task_io_service<detail::epoll_reactor<false> > impl_type; #elif defined(BOOST_ASIO_HAS_KQUEUE) typedef detail::task_io_service<detail::kqueue_reactor<false> > impl_type; #elif defined(BOOST_ASIO_HAS_DEV_POLL) typedef detail::task_io_service<detail::dev_poll_reactor<false> > impl_type; #else typedef detail::task_io_service<detail::select_reactor<false> > impl_type; #endif
原來是根據不同的平台支持特性,選擇了不同的實現,要把這么多種實現融合起來,沒有一個很好的架構是很難做到的。
我們再來看看 service_registry_,對這個成員變量的使用體現在 use_service、add_service、has_service 三個函數中:
template <typename Service> inline Service& use_service(io_service& ios) { // Check that Service meets the necessary type requirements. (void)static_cast<io_service::service*>(static_cast<Service*>(0)); (void)static_cast<const io_service::id*>(&Service::id); return ios.service_registry_->template use_service<Service>(); } template <typename Service> void add_service(io_service& ios, Service* svc) { // Check that Service meets the necessary type requirements. (void)static_cast<io_service::service*>(static_cast<Service*>(0)); (void)static_cast<const io_service::id*>(&Service::id); if (&ios != &svc->io_service()) boost::throw_exception(invalid_service_owner()); if (!ios.service_registry_->template add_service<Service>(svc)) boost::throw_exception(service_already_exists()); } template <typename Service> bool has_service(io_service& ios) { // Check that Service meets the necessary type requirements. (void)static_cast<io_service::service*>(static_cast<Service*>(0)); (void)static_cast<const io_service::id*>(&Service::id); return ios.service_registry_->template has_service<Service>(); }
看起來是個集合容器,里面的元素是服務(Service),服務有編號(id)。從 service_registry 的實現可以進一步了解到下面細節:
- 服務都是用一個類來實現的,唯一的接口要求是必須有 shutdown_service 接口
- 服務是延遲創建的,只有第一次被使用的時候才創建
- 每種類型的服務在同一個 service_registry (也是同一個io_service)里面最多只有一個實例
- 當service_registry (也就是io_service)被銷毀(析構)時,服務會被 shutdown ,然后被銷毀
繼續分析每個服務,還可以看到服務有下列特性:
- 服務在構造后就開始運作
- 服務在 shutdown 后停止運作
- 服務通過句柄(implementation_type)對外暴露其功能
- 服務之間有依賴性
最后,總結出所有的服務大概分為三類:
第一類服務是底層系統服務,是對操作系統平台提供功能的封裝,有:
- detail::win_iocp_io_service
- detail::win_iocp_socket_service
-
detail:: task_io_service
-
detail::reactive_socket_service
-
detail::epoll_reactor
-
detail::kqueue_reactor
-
detail::dev_poll_reactor
-
detail::select_reactor
-
detail::resolver_service
中間四個都是 reactor,不能想象,所有的 reactor 應該有相同的對外服務接口。這里的 task_io_service 和 reactive_socket_service 是對 reactor 的再封裝,所以上層的服務不會直接依賴 reactor,而是依賴 task_io_service 和 reactive_socket_service。
第二類服務是上層接口服務,面向具體的功能對象(Object),他們會針對運行平台選擇依賴對應的底層服務
- socket_acceptor_service(ip::basic_socket_acceptor -> ip::tcp::acceptor)
- stream_socket_service(ip::basic_stream_socket -> ip::tcp::socket)
- datagram_socket_service(ip::basic_datagram_socket -> ip::udp::socket)
- raw_socket_service(ip::basic_raw_socket -> ip::icmp::socket)
- deadline_timer_service(basic_deadline_timer -> deadline_timer)
- detail::deadline_timer_service
- ip::resolver_service(ip::basic_resolver -> ip::tcp::resolver, ip::udp::resolver, ip::icmp::resolver)
前四個 socket 相關的服務會在不同的平台,選擇依賴 win_iocp_socket_service 和 reactive_socket_service<xxx_reactor>,deadline_timer_service (具體實現在
detail::deadline_timer_service中)會選擇 win_iocp_io_service 和 task_io_service<xxx_reactor>,ip::resolver_service 沒得選擇,只有依賴 detail::resolver_service。
第三類服務是一些特殊功能的服務,比如 detail::strand_service 等,他們對整體框架沒有影響。
雖然 boost asio 中提供了這么多服務,但是上層應用並不會直接使用這些服務,服務通過句柄對外暴露其功能,而句柄被功能對象(Object)封裝,然后提供給上層應用使用。
這里的功能對象(Object),就是我們在第二類服務后面的“()”里面給出的類,每個對象都包含着一個對相應服務的C++引用,以及服務對外暴露的句柄。
至此,我們了解了 boost asio 中通過一系列的服務封裝了操作系統的底層功能,並且通過動態組裝的方式把這些服務管理起來。可以看出,boost asio 使用了一種相當簡單的方式,就解決了平台的多樣性,甚至於模式的多樣性;同時服務的動態加載和集中管理,為功能擴展提供了方便途徑;另外對象中只包含句柄,提高了安全性。
再回過頭來看看,我們心中還有個疑問,那就是 boost asio 是怎么實現前攝器(Proactor)模式的呢?其實前攝器(Proactor)的各個角色都是通過服務來表達的。
先看 windows 平台
Asynchronous Operation Processor 的功能是由 win_iocp_socket_service、resolver_service 完成,Proactor 功能由 win_iocp_io_service 完成,win_iocp_io_service 也包含 Asynchronous Event Demultiplexer 和 Completion Event Queue 的功能,不過其實是對 windows IOCP 的系統功能的封裝。
再看看 linux 平台
Asynchronous Operation Processor 的功能是由 reactive_socket_service、resolver_service 完成,Proactor、Asynchronous Event Demultiplexer 和 Completion Event Queue功能都是由 task_io_service 完成。
最后,io_service 其實代表了Proator 這一端,socket、timer、resolver 等等代表了 Initiator 這一端。這就是上層使用者角度看到的景象。