本篇文章在學習完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源碼。