io_service(在这里实现类为scheduler)需要一个监听描述符的epoll机制,而这个机制的实现,便是来源于epoll_reactor和descriptor_state,下面详细介绍。
descriptor_state保存所有描述符(就我观察应该都是socket描述符)的状态、处理函数handler、以及epoll相关数据等信息。而epoll_reactor算是Linux原生epoll和io_service之间的接口,核心逻辑当然还是调用原生的epoll,但是epoll_reactor借助descriptor_state对epoll监听的事件以及事件处理函数进行了很好的封装,内部实现了一套自动触发逻辑。
descriptor实际上继承自scheduler_operation,scheduler_operation是所有operation的基类,这个operation可以理解为各种各样的处理函数。所以descriptor中自然也就有相应的处理函数,一个是它自身的perform_io方法,用于在epoll_wait返回时触发的函数,该函数会传入一个uint32_t类型的参数,代表epoll触发的事件类型,内部会根据触发的事件类型去触发该descriptor绑定的描述符的同类型的处理函数;而另一个处理函数是全局静态的do_complete函数,这个函数没什么特别,仅用来触发perform_io,相当于外界调用的接口函数。
epoll_reactor虽然代码很多,但几乎都是操作已注册的描述符及其处理函数的方法,理解了descriptor的机制之后再来看epoll_reactor的这些操作方法就比较容易理解了,所以这里就不细讲了。
descriptor_state是以一种单链表的形式保存在epoll_reactor中的一个类型为object_poll的registered_descriptors_成员中,这个object_poll比较有意思,稍后再讲。
从descriptor_operation继承自scheduler_operation看来,它的语义依旧是一种处理函数,处理的是epoll_wait返回这一事件,而epoll_wait实际上是对其它事件的一种监听形式,所以对epoll真正监听到的事件,需要更具体地处理函数来处理,descriptor_operation就提供了一种组织管理这些具体的处理函数的机制。
descriptor_operation的源码实际不长,它是epoll_reactor的内部类:
class descriptor_state : operation
{
friend class epoll_reactor;
friend class object_pool_access; // 封装了操作descriptor_state链表的函数
descriptor_state* next_; // 方便用链表的形式来组织
descriptor_state* prev_;
mutex mutex_; // 对自身内部数据的访问锁
epoll_reactor* reactor_; // 对持有自身的epoll_reactor的引用
int descriptor_; // 描述符
uint32_t registered_events_; // 在epoll中注册的所有事件,这个事件是用位标志来描述的
op_queue<reactor_op> op_queue_[max_ops]; // 对所有的操作类型,每个操作类型维护一个队列。
bool try_speculative_[max_ops];
bool shutdown_; // 本描述符是否已关闭
BOOST_ASIO_DECL descriptor_state(bool locking);
void set_ready_events(uint32_t events) {
task_result_ = events; }
void add_ready_events(uint32_t events) {
task_result_ |= events; }
BOOST_ASIO_DECL operation* perform_io(uint32_t events);
BOOST_ASIO_DECL static void do_complete(
void* owner, operation* base,
const boost::system::error_code& ec, std::size_t bytes_transferred);
};
可以看到里面的op_queue就是epoll监听到的事件的处理函数reactor_op,这个队列是个数组,以epoll监听到的事件类型分类。reactor_op也是结成自scheduler_operation(operation是scheduler_operation的别名)
/* typedef scheduler_operation operation; */ // 另外某处的定义
class reactor_op
: public operation
{
public:
// The error code to be passed to the completion handler.
boost::system::error_code ec_;
// The number of bytes transferred, to be passed to the completion handler.
std::size_t bytes_transferred_;
// Status returned by perform function. May be used to decide whether it is
// worth performing more operations on the descriptor immediately.
enum status {
not_done, done, done_and_exhausted };
// Perform the operation. Returns true if it is finished.
status perform()
{
return perform_func_(this);
}
protected:
typedef status (*perform_func_type)(reactor_op*);
reactor_op(perform_func_type perform_func, func_type complete_func)
: operation(complete_func),
bytes_transferred_(0),
perform_func_(perform_func)
{
}
private:
perform_func_type perform_func_;
};
可以看得出reactor结构也很鉴定,仅是封装了一个处理函数,调用perform就相当于调用该处理函数。
这里再引入reactor_op的类型枚举类:
enum op_types {
read_op = 0, write_op = 1,
connect_op = 1, except_op = 2, max_ops = 3 };
可以看到max_ops也在此定义,说明操作类型最多就三种,write_op和connect_op的枚举值是一样的。
object_pool也可以看成是一个组织descriptor_state的数据结构,epoll_reactor的成员registered_descriptors_就是该类型,这个成员中保存着所有已经注册在该epoll中的描述符object_pool
接下来放object_pool的源码,里面有用到object_pool_access封装的操作函数,实际上非常简单,但为了完整性我还是一起放出来吧。
template <typename Object> // 这个传入的Object直接代入descriptor_state就行了
class object_pool
: private noncopyable
{
public:
// Constructor.
object_pool()
: live_list_(0),
free_list_(0)
{
}
// Destructor destroys all objects.
~object_pool()
{
destroy_list(live_list_);
destroy_list(free_list_);
}
// Get the object at the start of the live list.
Object* first()
{
return live_list_;
}
// Allocate a new object.
Object* alloc()
{
Object* o = free_list_;
if (o)
free_list_ = object_pool_access::next(free_list_);
else
o =