浅析boost asio中的event loop (4)

本文主要探讨boost asio在Linux环境下如何利用epoll_reactor进行事件循环。通过分析(descriptor_state)和run的实现,阐述异步操作如何缓存到op_queue,并讨论epoll相对于select的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们再来分析一下epoll_reactor

 

从前文我们知道boost asio会根据编译选项来选择reactor,对于Linux来说一般都是epoll,而对于2.4的内核就是select_reactor了,和Java里通过Interface来实现这种“策略模式”不同,C++一般是通过模版来实现,这种方式的好处就是不需要像Java中的Interface那么严格,可以有不同的返回类型或者不同的参数类型,这些接口可以是implicit的。

这些类都提供了这些接口:

init_task:初始化reactor
shutdown_service:关闭reactor
register_descriptor:注册文件fd
close_descriptor:关闭文件fd
start_op:开始执行op
cancel_ops:取消socket关联的所有op
add_timer_queue:
remove_timer_queue:
schedule_timer:
run:执行一次event loop

先来看看descriptor_state,descriptor_state是epoll中和一个socket关联,为每类操作都分配了一个op_queue,用户调用的异步操作都会先缓存在这些队列中,这点在后续的blog会有专门的分析

// connect和write在epoll中都是一样的可写状态
enum { read_op = 0, write_op = 1,
connect_op = 1, except_op = 2, max_ops = 3 };
// Per-descriptor queues.
struct descriptor_state
{
descriptor_state() {}
descriptor_state(const descriptor_state&) {}
void operator=(const descriptor_state&) {}
mutex mutex_;
op_queue<reactor_op> op_queue_[max_ops];
bool shutdown_;
};

然后我们在来看看run的实现

// Run epoll once until interrupted or events are ready to be dispatched.
void run(bool block, op_queue<operation>& ops)
{
 // Calculate a timeout only if timerfd is not used.
 int timeout;
 if (timer_fd_ != -1)
   timeout = block ? -1 : 0;
 else
 {
   mutex::scoped_lock lock(mutex_);
   // get_timeout返回离最近一个timer的时间,最多不超过5min
   timeout = block ? get_timeout() : 0;
 }
 // Block on the epoll descriptor.
 // 限制每次只处理128个events,这样会影响短连接性能?
 epoll_event events[128];
 int num_events = epoll_wait(epoll_fd_, events, 128, timeout);
 #if defined(BOOST_ASIO_HAS_TIMERFD)
 bool check_timers = (timer_fd_ == -1);
 #else // defined(BOOST_ASIO_HAS_TIMERFD)
 bool check_timers = true;
 #endif // defined(BOOST_ASIO_HAS_TIMERFD)
 // Dispatch the waiting events.
 for (int i = 0; i < num_events; ++i)
 {
   void* ptr = events[i].data.ptr;
   if (ptr == &interrupter_)
   {
  // No need to reset the interrupter since we're leaving the descriptor
  // in a ready-to-read state and relying on edge-triggered notifications
  // to make it so that we only get woken up when the descriptor's epoll
  // registration is updated.
 #if defined(BOOST_ASIO_HAS_TIMERFD)
  if (timer_fd_ == -1)
    check_timers = true;
 #else // defined(BOOST_ASIO_HAS_TIMERFD)
  check_timers = true;
 #endif // defined(BOOST_ASIO_HAS_TIMERFD)
   }
 #if defined(BOOST_ASIO_HAS_TIMERFD)
   else if (ptr == &timer_fd_)
   {
  check_timers = true;
   }
 #endif // defined(BOOST_ASIO_HAS_TIMERFD)
   else
   {
  descriptor_state* descriptor_data = static_cast<descriptor_state*>(ptr);
  mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
  // Exception operations must be processed first to ensure that any
  // out-of-band data is read before normal data.
  // 按优先级,从socket的各类队列中获取op,并执行(比如发送一些数据或者接收一些数据)
  static const int flag[max_ops] = { EPOLLIN, EPOLLOUT, EPOLLPRI };
  for (int j = max_ops - 1; j >= 0; --j)
  {
    if (events[i].events & (flag[j] | EPOLLERR | EPOLLHUP))
    {
   while (reactor_op* op = descriptor_data->op_queue_[j].front())
   {
     if (op->perform())
     {
    descriptor_data->op_queue_[j].pop();
    // 执行完成的op存放在函数的返回值ops中,然后在task_io_service中对op执行complete操作
    ops.push(op);
     }
     else
    break;
   }
    }
  }
   }
 }
 if (check_timers)
 {
   mutex::scoped_lock common_lock(mutex_);
   // 将到期的timer放入函数的返回值ops中,然后在task_io_service中对op执行complete操作
   timer_queues_.get_ready_timers(ops);
 #if defined(BOOST_ASIO_HAS_TIMERFD)
   if (timer_fd_ != -1)
   {
  itimerspec new_timeout;
  itimerspec old_timeout;
  int flags = get_timeout(new_timeout);
  timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout);
   }
 #endif // defined(BOOST_ASIO_HAS_TIMERFD)
 }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值