本篇文章在学习完Muduo之后来记录自己对这几个类的理解。如有理解不当的地方还请各位朋友指出,感激不尽!
我们先将这三个类的关系理清楚,然后我们再讲解源码的具体实现。
Eventloop类
- Muduo是one loop per thread 模型。
- Eventloop类可以看作为一个指挥家,它不负责具体的事物实现,只负责将这个程序有条不紊的运作起来,Eventloop类不用知道你具体怎么实现的,只需要知道你这个接口是要做什么的,我负责将这一类的东西分发到你这里。
Channel类
- 通道看名字就有了大概的理解。在日常生活中,通道就是你去做事情的路径,我去北京当然要做上去北京的高铁,去西安要坐上去西安的高铁,你去北京的“通道”是去坐上了西安的高铁,那你可能这辈子都到不了。
- Channel类就有了大致的理解,它封装了网络编程中的“通道”,网络编程中需要什么通道呢?大体就是可读、可写、错误处理事件,那Channel类就是将这些事情都集于一身形成了一个总通道。
EPollPoller类
- 该类就是对我们平常使用的epoll进行了封装。它在Muduo中继承了Poller类,一会讲源码的时候我们可以看到。
现在知道了各个类的作用,那我们就来看看Muduo中是如何将这三者联系起来的。
首先看看EventLoop类的成员
typedef std::vector<Channel*> ChannelList;
bool looping_;
std::atomic<bool> quit_;
bool eventHandling_;
bool callingPendingFunctors_;
int64_t iteration_;
const pid_t threadId_; //线程ID
Timestamp pollReturnTime_; //对时间的封装
std::unique_ptr<Poller> poller_; //用unique_ptr是因为poller是个基类,需要动态绑定
std::unique_ptr<TimerQueue> timerQueue_; //定时器类
int wakeupFd_; //唤醒Eventloop所在IO线程的fd
// unlike in TimerQueue, which is an internal class,
// we don't expose Channel to client.
std::unique_ptr<Channel> wakeupChannel_; //用于处理wakeupfd_上的readable事
ChannelList activeChannels_; //保存活动的Channel类
Channel* currentActiveChannel_;
看看EventLoop::loop()函数,"指挥官"的主逻辑函数
void EventLoop::loop()
{
assert(!looping_);
assertInLoopThread(); //当事件循环时,需要检查该线程是否为IO线程
looping_ = true; //运行时将该成员设为true
quit_ = false; // FIXME: what if someone calls quit() before loop() ? quit函数我们下面会讲到
LOG_TRACE << "EventLoop " << this << " start looping";
while (!quit_)
{
//先将channellist清空
activeChannels_.clear();
//这个相当于调用epoll_wait然后将events存入到了activeChannel中。(这篇文章讲的是epoll的实现)
pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
++iteration_;
//日志我们现在不需要关注它,略过
if (Logger::logLevel() <= Logger::TRACE)
{
printActiveChannels();
}
// TODO sort channel by priority
eventHandling_ = true;
//这里就是指挥官分发channel,由channel去自己处理自己的事件
for (Channel* channel : activeChannels_)
{
currentActiveChannel_ = channel;
currentActiveChannel_->handleEvent(pollReturnTime_);
}
currentActiveChannel_ = NULL;
eventHandling_ = false;
doPendingFunctors();
}
LOG_TRACE << "EventLoop " << this << " stop looping";
looping_ = false;
}
void EventLoop::quit()
{
quit_ = true; //在设置之后loop函数不是立即退出,而是需要在下一次while判断才会退出,如果loop阻塞在某个调用,则会导致延迟退出
// There is a chance that loop() just executes while(!quit_) and exits,
// then EventLoop destructs, then we are accessing an invalid object.
// Can be fixed using mutex_ in both places.
if (!isInLoopThread())
{
wakeup();
}
}
看一下EPollPoller的成员
EPollPoller继承于Poller的,Poller的源码比较短大家自行去阅读,我把Poller中的成员放到EpollPoller中来统一说明
typedef std::vector<struct epoll_event> EventList;
int epollfd_;
EventList events_
typedef std::map<int, Channel*> ChannelMap;
ChannelMap channels_;//用来存放fd->channel的映射关系(每一个fd对于一个自己的channel类)
EventLoop* ownerLoop_
Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels)
{
LOG_TRACE << "fd total count " << channels_.size();
int numEvents = ::epoll_wait(epollfd_,
&*events_.begin(),
static_cast<int>(events_.size()),
timeoutMs); //epoll_wait函数,用events_来接收
int savedErrno = errno;
Timestamp now(Timestamp::now());
if (numEvents > 0)
{
LOG_TRACE << numEvents << " events happened";
//调用fillActiveChannels函数将收到的活动事件加入到activeChannels中
fillActiveChannels(numEvents, activeChannels);
//如果接收到的事件大小和evnts_.size()相等,将其扩容。
if (implicit_cast<size_t>(numEvents) == events_.size())
{
events_.resize(events_.size()*2);
}
}
else if (numEvents == 0)
{
LOG_TRACE << "nothing happened";
}
else
{
// error happens, log uncommon ones
if (savedErrno != EINTR)
{
errno = savedErrno;
LOG_SYSERR << "EPollPoller::poll()";
}
}
return now;
}
void EPollPoller::fillActiveChannels(int numEvents,
ChannelList* activeChannels) const
{
assert(implicit_cast<size_t>(numEvents) <= events_.size());
for (int i = 0; i < numEvents; ++i)
{
//从events_[i]中取得其对应的channel指针
Channel* channel = static_cast<Channel*>(events_[i].data.ptr);
#ifndef NDEBUG
int fd = channel->fd();
ChannelMap::const_iterator it = channels_.find(fd);
assert(it != channels_.end());
assert(it->second == channel);
#endif
channel->set_revents(events_[i].events);
activeChannels->push_back(channel); //将其加入到activeChannels中(activeChannels由前面的EventLoop中传入)
}
}
Channel类的成员
static const int kNoneEvent;
static const int kReadEvent;
static const int kWriteEvent;
EventLoop* loop_;
const int fd_; //保存套接字fd
int events_;
int revents_; //epoll返回的该channel类对应事件的events(用来判断其可读可写还是错误连接)
int index_; // used by Poller. pooler中poolfd的数组下标,为了方便找寻该channel的位置,在epoll中则不需要该成员,因为epoll底层用红黑数实现,不需要保存其下标。
bool logHup_;
std::weak_ptr<void> tie_;
bool tied_;
bool eventHandling_;
bool addedToLoop_;
//通道的不同回调函数(就是我们前面说明的不同类型的事件调用不同类型的函数)
ReadEventCallback readCallback_;
EventCallback writeCallback_;
EventCallback closeCallback_;
EventCallback errorCallback_;
};
Channel类中和EventLoop有关系的成员函数
void Channel::handleEvent(Timestamp receiveTime)
{
std::shared_ptr<void> guard;
if (tied_)
{
guard = tie_.lock();
if (guard)
{
handleEventWithGuard(receiveTime);
}
}
else
{
handleEventWithGuard(receiveTime);
}
}
//来判断事件的类型来执行所对应的回调函数
void Channel::handleEventWithGuard(Timestamp receiveTime)
{
eventHandling_ = true;
LOG_TRACE << reventsToString();
if ((revents_ & POLLHUP) && !(revents_ & POLLIN))
{
if (logHup_)
{
LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLHUP";
}
if (closeCallback_) closeCallback_();
}
if (revents_ & POLLNVAL)
{
LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLNVAL";
}
if (revents_ & (POLLERR | POLLNVAL))
{
if (errorCallback_) errorCallback_();
}
if (revents_ & (POLLIN | POLLPRI | POLLRDHUP))
{
if (readCallback_) readCallback_(receiveTime);
}
if (revents_ & POLLOUT)
{
if (writeCallback_) writeCallback_();
}
eventHandling_ = false;
}
总结
看到这里我们来总结一下三个类的关系。
- EventLoop::loop()中调用EPollPoller::poll()来获得活动的Channel集合
- EventLoop::loop()再将获得的Channel集合进行分发
- 分发完的Channel执行自己的handleEvent函数
- handleEvent函数在去判断对应的事件去分发回调函数
至此我们将三个类的整体流程进行了讲解和分析,详细的内容还是要自己去看Muduo源码。