ASIO 之 剖 析--(4) 以Proactor模式的角度来剖析ASIO

MORE INFO AT : WWW.LEEHAO.ORG

___________________________________________________________________________________________________________________________________

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 在该文件中完成了对于异步读写端口函数的注册。用以注册其所服务的函数句柄。

 

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类的结构如下,其中列举了重要的相关数据结构:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值