(本文基於Boost 1.69)
Boost.Asio代碼風格。Asio爲了可讀性將部分較複雜的類的聲明和實現分紅了兩個頭文件,在聲明的頭文件末尾include負責實現的頭文件。impl文件夾包含這些實現的頭文件。另外,還有一個常見的關鍵詞是detail,不一樣操做系統下的各類具體的代碼會放在detail文件夾下。
先看io_context
。io_context
的主要功能:接受任務,處理io事件,回調。
閱讀源碼發現,io_context
包含一個io_context具體實現,在windows平臺是win_iocp_io_context
,實現了原生的proactor,其餘平臺則是一個scheduler
。react
// file: <boost/asio/io_context.hpp> ... #if defined(BOOST_ASIO_HAS_IOCP) typedef class win_iocp_io_context io_context_impl; class win_iocp_overlapped_ptr; #else typedef class scheduler io_context_impl; #endif ...
咱們來看看scheduler
。scheduler
包含一個reactor
,scheduler經過reactor模擬proactor:用戶面對的接口一致,但數據的複製是在用戶態而非內核態完成。
題外話:linux也有異步io接口,但相比windows iocp並不成熟。另外瀏覽源碼可知,reactor在不一樣平臺的實現也不一樣,其中linux實現爲基於epoll
的epoll_reactor
,macOS實現爲基於kqueue
的kqueue_reactor
。linux
// file: <boost/asio/detail/reactor_fwd.hpp> ... #if defined(BOOST_ASIO_HAS_IOCP) || defined(BOOST_ASIO_WINDOWS_RUNTIME) typedef class null_reactor reactor; #elif defined(BOOST_ASIO_HAS_IOCP) typedef class select_reactor reactor; #elif defined(BOOST_ASIO_HAS_EPOLL) typedef class epoll_reactor reactor; #elif defined(BOOST_ASIO_HAS_KQUEUE) typedef class kqueue_reactor reactor; #elif defined(BOOST_ASIO_HAS_DEV_POLL) typedef class dev_poll_reactor reactor; #else typedef class select_reactor reactor; #endif ...
scheduler
調度任務。首先介紹與scheduler密切相關的變量:windows
scheduler
成員op_queue_
:操做隊列scheduler
成員task_
:reactor與scheduler
相關的隊列有兩個,其中scheduler
的操做隊列用於存放通常性操做,而線程私有隊列存放與reactor直接相關的操做。這兩類操做須要分別處理,分開放在兩個隊列。查看Asio源碼可知,scheduler::run
調用 scheduler::do_run_once
,其中reactor的成員函數run
接受線程私有隊列。app
// file: <boost/asio/detail/impl/scheduler.ipp> ... std::size_t scheduler::do_run_one(mutex::scoped_lock& lock, scheduler::thread_info& this_thread, const boost::system::error_code& ec) { while (!stopped_) { if (!op_queue_.empty()) { // Prepare to execute first handler from queue. operation* o = op_queue_.front(); op_queue_.pop(); bool more_handlers = (!op_queue_.empty()); if (o == &task_operation_) { task_interrupted_ = more_handlers; if (more_handlers && !one_thread_) wakeup_event_.unlock_and_signal_one(lock); else lock.unlock(); task_cleanup on_exit = { this, &lock, &this_thread }; (void)on_exit; // Run the task. May throw an exception. Only block if the operation // queue is empty and we're not polling, otherwise we want to return // as soon as possible. task_->run(more_handlers ? 0 : -1, this_thread.private_op_queue); } else { std::size_t task_result = o->task_result_; if (more_handlers && !one_thread_) wake_one_thread_and_unlock(lock); else lock.unlock(); // Ensure the count of outstanding work is decremented on block exit. work_cleanup on_exit = { this, &lock, &this_thread }; (void)on_exit; // Complete the operation. May throw an exception. Deletes the object. o->complete(this, ec, task_result); return 1; } } else { wakeup_event_.clear(lock); wakeup_event_.wait(lock); } } return 0; } ...
通常性的operation能夠直接傳給scheduler
,可是reactor是scheduler
的一個私有成員,那麼用戶如何讓reactor完成相關io操做,或者說,用戶如何生成一個針對reactor的operation(還須要考慮跨平臺的問題,在windows平臺是針對基於iocp的proactor的operation)?這裏須要一個額外的抽象,具體來講,Asio經過服務來提供某類型的通常性接口。例如,查看Asio源碼可知,unix平臺下的reactive_socket_service
和windows平臺的win_iocp_socket_service
就實現了基於reactor/proactor的io操做的接口,包括建立socket,異步發送、接收等,其中Handler爲io操做完成後的回調類。有了這些服務,ip::tcp::socket
、ip::udp::socket
等具體實現只要調用service統一的接口來間接操做reactor/proactor便可。異步
// file: <boost/asio/detail/reactive_descriptor_service.hpp> template <typename Protocol> class reactive_socket_service : public service_base<reactive_socket_service<Protocol> >,public reactive_socket_service_base { ... template <typename Handler> void async_send_to(implementation_type& impl, const null_buffers&, const endpoint_type&, socket_base::message_flags, Handler& handler) ... } // file: <boost/asio/detail/win_iocp_socket_service.hpp> template <typename Protocol> class win_iocp_socket_service :public service_base<win_iocp_socket_service<Protocol> >,public win_iocp_socket_service_base { ... template <typename Handler> void async_send_to(implementation_type& impl, const null_buffers&,const endpoint_type&, socket_base::message_flags, Handler& handler) ... }
服務「模式」的具體實現:服務訪問io_context內部的方式和註冊服務。
服務訪問io_context內部具體實現。Asio經過friend聲明讓其餘symbol來訪問io_context的私有成員以及io_context私有成員scheduler的私有成員
註冊服務簡介。Asio包含一個註冊服務類service_registry。查看Asio源碼可知,service_registry包含一個service鏈表,service_registry的owner(其中execution_context是io_context的基類)。socket
// file: <boost/asio/detail/service_registry.hpp> class service_registry: private noncopyable { public: // Constructor. BOOST_ASIO_DECL service_registry(execution_context& owner); ... private: ... // The owner of this service registry and the services it contains. execution_context& owner_; // The first service in the list of contained services. execution_context::service* first_service_; };
註冊服務具體實現。顯而易見地,圍繞服務須要實現的功能有:
獲取服務與註冊服務。顯然用戶最關心的是如何方便的獲取服務。用戶獲取服務時提供的參數是服務的類(做爲函數模版的模版參數),查看源碼可知,asio經過typeid
或服務類的靜態數據成員id
的地址將類轉化爲具體的key,經過key進行服務的註冊和查找。
生成服務。服務的構造參數爲io_context,在堆上構造而後保存指針便可,這部分比較簡單,很少做敘述。async
// file: <boost/asio/execution_context.hpp> ... struct key { key() : type_info_(0), id_(0) {} const std::type_info* type_info_; const execution_context::id* id_; } key_; ... // file: <boost/asio/detail/impl/service_registry.hpp> ... template <typename Service> void service_registry::init_key_from_id(execution_context::service::key& key,const service_id<Service>& /*id*/) { key.type_info_ = &typeid(typeid_wrapper<Service>); key.id_ = 0; } ... // file: <boost/asio/detail/impl/service_registry.ipp> ... void service_registry::init_key_from_id(execution_context::service::key& key,const execution_context::id& id) { key.type_info_ = 0; key.id_ = &id; } ...
爲何使用服務「模式」?顯然的,Asio的設計受到了proactor/reactor(跨平臺)自己的影響,額外的抽象對於處理跨平臺是頗有必要的。服務模式使得Asio庫的接口更具備組織性,層次性,靈活性。具體來講:tcp
ip::tcp::socket
和io_context
的公開成員函數,io_context
內部proactor實現對用戶隱藏ip::tcp::socket
、ip::udp::socket
等的開發人員經過相關service提供的成員函數間接與proactor/reactor交互來實現各類io功能其餘方案?筆者目前水平有限,暫時想不到更好的方法。函數