我们再来分析一下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)
}
}