MORE INFO AT : WWW.LEEHAO.ORG
___________________________________________________________________________________________________________________________________l io_service
其主要是充当proactor角色。我们在io_service.hpp 中可以看到对于其是否使用IOCP模式,其有两种不同的方式方法:windows平台下,非windows平台下。在windows平台下使用的是完成端口形式iocp (IO completion port),而在非windows平台下使用的是select/poll/epoll的方式。用以实现 asio的跨平台的高性能io操作。由于select模型每次需要内核检查所有io handle的状态,导致其效率相对低下,在以后的设计中推荐使用poll/epoll方式[Xnix推荐标准]。而poll及epoll则在内核获得特定handle的状态变化后,通知poll/epoll并返回其有效hanlde,从而无需内核检测所有的handle,从而进一步的提高了poll/epoll的轮询效率。
从以下的io_service.hpp文件中我们可以看出对于平台的区别。
#ifdefined(BOOST_ASIO_HAS_IOCP)
#include <boost/asio/detail/win_iocp_io_service_fwd.hpp> //使用win平台下的iocp 服务。
#else
#include <boost/asio/detail/task_io_service_fwd.hpp> //对于非win平台。
#endif
#ifdefined(BOOST_ASIO_HAS_IOCP)
namespacedetail { typedef win_iocp_io_serviceio_service_impl;}
#else
namespacedetail { typedef task_io_service io_service_impl;}
#endif
其io_service的定义如下,从下面蓝色代码,我们可以看出其完成了完成事件的注册,使得事件完成后所回调的函数与该完成事件能够进行正确的映射:
class io_service
:private noncopyable
{
private:
typedef detail::io_service_impl impl_type;
#ifdefined(BOOST_ASIO_HAS_IOCP)
friend class detail::win_iocp_overlapped_ptr;
#endif
public:
class work;
friend class work;
class id;
class service;
class strand;
….
private:
#ifdefined(BOOST_WINDOWS) || defined(__CYGWIN__)
detail::winsock_init<>init_;
#elifdefined(__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_HAS_IOCP宏的定义在/asio/detail/config.hpp文件给出[上文已经给出其详细的说明]。在该文件中给出了不同平台下所应该使用的IO方式。如windows平台下的完成端口方式,以及Linux下的select/poll/epoll方式,以及Mac OS, FreeBSD, NetBSD, OpenBSD平台下的kqueue方式,Solaris 下的/dev/poll方式。
其中对于io_service我们可以追溯到asio/detail/impl目录下的task_io_service.ipp及win_iocp_io_service.ipp这两个文件。其中task_io_service为使用posix方式完成,而win_iocp_io_service.ipp则是在windows平台下的完成端口形式。其中的io_service.run()则提供了上层用户其与OS服务之间的桥梁。detail/impl/service_registry.ipp 在该文件中完成了对于异步读写端口函数的注册。用以注册其所服务的函数句柄。
l win_iocp_io_service
在win_iocp_io_service.ipp文件中的do_one函数,我们可以看出 其主要是完成上层用户所提交的操作,如完成端口的io操作,定时操作。
而在task_io_service.ipp文件中则是Xnix下的完成IO操作。同样,文件中的do_one其与win_iocp_io_service.ipp文件中的do_one起着相同的作用。
首先,我们来分析一下Xnix下的实现,其主要是在task_io_service.ipp文件中完成。而Xnix下使用的是reactor模式来完成。 task_io_service的结构如下。
idle_thread_info 为空闲线程情况链表,其中存放那些可以进行有效的IO读写的空闲线程。
下面系统所使用的几个重要的数据结构的定义:
structtask_io_service::idle_thread_info
{
event wakeup_event; //事件描述
idle_thread_info* next; //下一个对象。
};
structtask_io_service::task_cleanup //完成任务的清除工作。
{
~task_cleanup()
{
// Enqueue the completed operations andreinsert the task at the end of
// the operation queue.
lock_->lock();
task_io_service_->task_interrupted_ =true;
task_io_service_->op_queue_.push(*ops_);
task_io_service_->op_queue_.push(&task_io_service_->task_operation_);
}
task_io_service* task_io_service_;
mutex::scoped_lock* lock_;
op_queue<operation>* ops_;
};
structtask_io_service::work_finished_on_block_exit
{
~work_finished_on_block_exit()
{
task_io_service_->work_finished();
}
task_io_service* task_io_service_;
};
而其中run/run_one/poll/poll_one等函数均调用do_one来完成具体的io任务。下面是do_one的主要描述:
std::size_t task_io_service::do_one(mutex::scoped_lock& lock,
task_io_service::idle_thread_info* this_idle_thread)
{
while (!stopped_)
{
if (!op_queue_.empty()) //操作事件队列,为Proactor模式中的Completion EventQueue
{
// Prepare to execute first handler fromqueue.
operation* o = op_queue_.front();
op_queue_.pop();
bool more_handlers =(!op_queue_.empty());
if (o == &task_operation_) //判定任务队列中是否具有未完成的操作。
{
task_interrupted_ = more_handlers ||polling;
// If the task has already run andwe're polling then we're done.
if (task_has_run && polling)
{
task_interrupted_ = true;
op_queue_.push(&task_operation_);
return 0;
}
task_has_run = true;
if (!more_handlers ||!wake_one_idle_thread_and_unlock(lock)) //请求一个空闲的线程,并对其进行解锁并用来服务该项。
lock.unlock();
op_queue<operation>completed_ops;
task_cleanup c = { this, &lock,&completed_ops };
(void)c;
// Run the task. May throw anexception. Only block if the operation
// queue is empty and we're notpolling, otherwise we want to return
// as soon as possible.
task_->run(!more_handlers&& !polling, completed_ops);
}
else
{
if (more_handlers)
wake_one_thread_and_unlock(lock);
else
lock.unlock();
// Ensure the count of outstanding workis decremented on block exit.
work_finished_on_block_exit on_exit = {this };
(void)on_exit;
// Complete the operation. May throw anexception.
o->complete(*this); // deletes theoperation object
return 1;
}
}
else if (this_idle_thread)
{
// Nothing to run right now, so just waitfor work to do.
this_idle_thread->next =first_idle_thread_;
first_idle_thread_ = this_idle_thread;
this_idle_thread->wakeup_event.clear(lock);
this_idle_thread->wakeup_event.wait(lock);
}
else
{
return 0;
}
}
return 0;
}
在windows平台下由win_iocp_io_service.hpp及win_iocp_io_service.ipp来定义其相关的完成端口操作。在windows平台下,我们首先要创建一个IO完成端口,在其端口创建成功后,我们需要将我们所需要监视的IO端口进行关联注册,使其与一个完成端口相关联。
其相关工作由CreateIoCompletionPort 函数来完成。而通过函数GetQueuedCompletionStatus函数来完成端口上等待下一个IO Package。而boost下的完成端口类型的核心功能由win_iocp_io_service类来完成。下面是win_iocp_io_service.hpp文件中涉及到的几个重要的数据结构的定义。
structwin_iocp_io_service::work_finished_on_block_exit
{
~work_finished_on_block_exit()
{
io_service_->work_finished();
}
win_iocp_io_service* io_service_;
};
struct win_iocp_io_service::timer_thread_function //由定时器所触发的操作。
{
void operator()()
{
while (::InterlockedExchangeAdd(&io_service_->shutdown_,0) == 0)
{
if (::WaitForSingleObject(io_service_->waitable_timer_.handle,INFINITE) == WAIT_OBJECT_0)//等待消息的到来,否则阻塞程序的调用。
{
::InterlockedExchange(&io_service_->dispatch_required_,1);
::PostQueuedCompletionStatus(io_service_->iocp_.handle,
0,wake_for_dispatch, 0);
}
}
}
win_iocp_io_service*io_service_;
win_iocp_io_service类结构如下[略去数据成员]:
作为该类得核心函数do_one的描述如下:首先其轮询所以的待处理的操作或者是定时器,若存在待处理的则调用函数GetQueuedCompletionStatus来获得具体的操作及其完成的信息。
for (;;)
{
// Try to acquire responsibility fordispatching timers and completed ops.
if (::InterlockedCompareExchange(&dispatch_required_,0, 1) == 1)
{
mutex::scoped_lock lock(dispatch_mutex_);
// Dispatch pending timers andoperations.
op_queue<win_iocp_operation>ops;
ops.push(completed_ops_);
timer_queues_.get_ready_timers(ops);
post_deferred_completions(ops);
update_timeout();
}
// Get the next operation from the queue.
DWORD bytes_transferred = 0;
dword_ptr_t completion_key = 0;
LPOVERLAPPED overlapped = 0;
::SetLastError(0);
BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle,&bytes_transferred,
&completion_key, &overlapped, block ? gqcs_timeout : 0);
DWORD last_error = ::GetLastError();
if (overlapped)
{
win_iocp_operation* op =static_cast<win_iocp_operation*>(overlapped);
boost::system::error_coderesult_ec(last_error,
boost::asio::error::get_system_category());
// We may have been passed the last_errorand bytes_transferred in the
// OVERLAPPED structure itself.
if (completion_key ==overlapped_contains_result)
{
result_ec =boost::system::error_code(static_cast<int>(op->Offset),
*reinterpret_cast<boost::system::error_category*>(op->Internal));
bytes_transferred = op->OffsetHigh;
}
// Otherwise ensure any result has beensaved into the OVERLAPPED
// structure.
else
{
op->Internal =reinterpret_cast<ulong_ptr_t>(&result_ec.category());
op->Offset = result_ec.value();
op->OffsetHigh = bytes_transferred;
}
// Dispatch the operation only if ready.The operation may not be ready
// if the initiating function (e.g. acall to WSARecv) has not yet
// returned. This is because theinitiating function still wants access
// to the operation's OVERLAPPED structure.
if (::InterlockedCompareExchange(&op->ready_,1, 0) == 1)
{
// Ensure the count of outstanding workis decremented on block exit.
work_finished_on_block_exit on_exit = {this };
(void) on_exit;
op->complete(*this,result_ec, bytes_transferred);
ec = boost::system::error_code();
return 1;
}
}
else if (!ok)
{
if (last_error != WAIT_TIMEOUT)
{
ec = boost::system::error_code(last_error,
boost::asio::error::get_system_category());
return 0;
}
// If we're not polling we need to keepgoing until we get a real handler.
if (block)
continue;
ec = boost::system::error_code();
return 0;
}
else if (completion_key ==wake_for_dispatch)
{
// We have been woken up to try toacquire responsibility for dispatching
// timers and completed operations.
}
else
{
// The stopped_ flag is always checked toensure that any leftover
// interrupts from a previous runinvocation are ignored.
if (::InterlockedExchangeAdd(&stopped_,0)!= 0)
{
// Wake up next thread that is blockedon GetQueuedCompletionStatus.
if (!::PostQueuedCompletionStatus(iocp_.handle,0, 0, 0))
{
last_error = ::GetLastError();
ec =boost::system::error_code(last_error,
boost::asio::error::get_system_category());
return 0;
}
ec = boost::system::error_code();
return 0;
}
}
}
至此我们讨论了不同平台下对于完成端口的实现一些细节性的内容,希望能够给大家关于boost:asio底层运作机制的一个初步概况,对于非Windows平台下所使用的task_io_service则可在asio/detail/及asio/detail/impl目录下找到其相关源码。task_io_service类的结构如下,其中列举了重要的相关数据结构: