muduo事件循环分析
muduo网络库网络I/O模型为非阻塞reactor模式。使用linux的epoll或poll系统调用,轮寻多个socket,然后利用事先注册的事件句柄处理发生事件的套接字。
muduo中一个thread与一个EventLoop绑定,即“one-loop-per-thread”模式,线程的主循环核心代码:
while(1){ 1) poller_->poll(); 2) for(...){ /*处理发生事件的socket*/ handleEvent(); } 3) doPendingFunctors(); } |
其中,muduo使用timerfd定时器描述符,可以像处理普通文件描述符一样处理定时器。因此1)、2)两个步骤完成文件描述符的同步处理操作。而步骤3)执行函数列表中积压的函数对象(muduo规定,一个socket的事件句柄只能由其注册的线程调用,如果其他线程想要调用必须将包含参数的回调函数对象投递到目标线程时间循环的函数列表中异步执行),完成异步操作。
事件循环中的定时器
muduo库中的定时器采用timerfd,因此需要使用select,poll,epoll等检测,另外muduo的定时器不是严格定时器。为了实现定时器功能,涉及的对象主要有Timer,Timestamp,TimerQueue。其中Timer对象为定时器本体,包括定时器时限和超时回调函数;Timestamp是时间戳,主要用于比较定时器到期否;TimerQueue对象用于存储Timer(使用二叉搜索树set<>存储),并在EventLoop中注册channel,定时器超时后调用handleRead函数,依次处理所有超时定时器。
启动TimerQueue
>muduo_net_::EventLoop loop
| >timerQueue_(new TimerQueue(this)) //构造TimerQueue对象
| | >timerfd_(createTimerfd())
| | | >return ::timerfd_create(CLOCK_MONOTONIC,TFD_NONBLOCK | TFD_CLOEXEC) //申请timefd描述符
| >timerfdChannel_(loop, timerfd_) // 关联timerfd_的Channel对象
| >timerfdChannel_.setReadCallback(boost::bind(&TimerQueue::handleRead, this)); //绑定超时处理
| >timerfdChannel_.enableReading(); //为timerfd_描述符添加poll事件源
添加定时器Timer,调用EventLoop::runAt \ runAfter \ runEvery均会调用TimerQueue::addTimer函数
>timerQueue_->addTimer(cb, time, interval);
| >Timer* timer = new Timer(cb, when, interval); // 构造新的定时器对象
| >loop_->runInLoop(boost::bind(&TimerQueue::addTimerInLoop, this, timer)); //添加定时器到EventLoop中
| |>bool earliestChanged = insert(timer); // 将Timer对象插入set<>中
| | >resetTimerfd(timerfd_, timer->expiration()); // 重置timerfd描述符
定时器超时处理
>currentActiveChannel_->handleEvent(pollReturnTime_); //在EventLoop的loop函数中调用
| >即调用TimerQueue::handleRead() //timerfd的事件处理函数
| | >std::vector<Entry> expired = getExpired(now); //查找set<>中所有超时的定时器
| | >for (std::vector<Entry>::iterator it = expired.begin(); it != expired.end(); ++it)
| | | >it->second->run(); //即调用Timer::callback_()
| | >reset(expired, now); //清除已超时定时器,并跟新timefd描述符