muduo採用的是one Loop per Thread 的模式,即每個線程中有一個主循環,再配合着IO多路複用,即可實現在一個線程中同時監聽多個fd。主要利用EventLoop{.h/.cc} Channel{.h/.cc} Epoll{.h/.cc}來實現Reactor模式。
回顧Reactor事件處理模式
Reactor模式是瞭解高性能IO的最基礎模式,其主要的思想是,將需要處理的IO事件註冊到一個IO多路複用的fd上,讓單線程/單進程來監聽這個fd,如果fd上監聽的事件被觸發則立刻調用不同事件的處理器(回調函數)進行處理。
在c++11中,可以簡單使用function/bind來實現回調關係,使得編碼更加簡潔。
EventLoop類具體分析
class EventLoop{
public:
EventLoop();
~EventLoop();
void loop();
void assertInLoopThread(){
if(!isInLoopThread()){
abortNotInLoopThread();
}
}
EventLoop* getEventLoopOfCurrentThread();
bool isInLoopThread() const { return threadId_ ==static_cast<pid_t>(pthread_self());}
void updateChannel(Channel* channel);
void removeChannel(Channel* channel);
void quit();
private:
typedef std::vector<Channel*> ChannelList;
void abortNotInLoopThread();
bool loop_;
const pid_t threadId_;
std::unique_ptr<Epoll> poller_;
bool quit_;
ChannelList activeChannels_;
};
EventLoop中最重要的loop函數,裏面有一個循環調用poller->poll(),每次都將activesChannels_傳入poll函數來獲得在本次循環中觸發事件的channel,然後更具事件的不同種類(新連接,可讀,可寫)來分別調用不同的觸發器。
void EventLoop::loop(){
...
while (!quit_)//one loop per thread指的就是這個loop
{
activeChannels_.clear();
//pollreturntime = poller_->poll(kPollTimes,&activeChannels_);//這裏調用poll看有無發生事件,如果有則放到了activechannels_
poller_->poll(kPollTimes,&activeChannels_);//先不用Timestamp
for(ChannelList::iterator it=activeChannels_.begin();it!=activeChannels_.end();++it){
(*it)->handleEvent();//進行處理事件
}
}
...
}
Channel類具體分析
Channel類其實是fd的一個抽象,即每個Channel對應一個fd及對其操作。
class Channel : noncopyable{
public:
typedef function<void()> EventCallback;
Channel(EventLoop* loop,int fd);
void handleEvent(); //根據revents的值調用不同的用戶調用
void setReadCallback(const EventCallback& cb){
readcallback_=cb;
}
void setWriteCallback(const EventCallback& cb){
writecallback_=cb;
}
void setErrorCallback(const EventCallback& cb){
errorcallback_=cb;
}
int fd() const{ return fd_; }
int events() const { return events_; }
void set_revents(int revt) { revents_=revt; }
bool isNoneEvent() const { return events_ == kNoneEvent; }
void enableReading() {events_ |= kReadEvent; update(); }
void enableWriting() {events_ |= kWriteEvent; update(); }
void disableWriting() {events_ &= ~kWriteEvent; update(); }
void diableAll() {events_ = kNoneEvent; update(); }
int index() const { return index_; }
void set_index(int index) { index_ = index; }
EventLoop* ownerLoop() const { return loop_; }
private:
void update();//用於更改完events後的更新操作
static const int kNoneEvent;
static const int kReadEvent;
static const int kWriteEvent;
EventLoop* loop_;//每個channel對應一個EventLoop
const int fd_;//每個channel對應一個fd
int events_; //fd關心的io事件
int revents_;//目前活動的事件
int index_; //kAdded(已經監聽),kNew(新建),kDeleted(已經被刪除),用於體現當前fd的狀態
EventCallback readcallback_;
EventCallback writecallback_;
EventCallback errorcallback_;
};
當事件分發器分發好不同的觸發事件種類後,就要調用Channel::handleEvent()來處理。readcallback_,writecallback_,errorcallback_都是之前通過setReadCallback(),setWriteCallback(),setErrorCallback()註冊過的,這裏即可體現出Reactor事件處理模式的核心思想。
void Channel::handleEvent(){
if(revents_ & EPOLLERR) {
if(errorcallback_) errorcallback_();
}
if(revents_ & (EPOLLIN | EPOLLPRI | EPOLLRDHUP)){
if(readcallback_) readcallback_();
}
if(revents_ & EPOLLOUT){
if(writecallback_) writecallback_();
}
}
Epoll類具體分析
class Epoll : noncopyable{
public:
typedef std::vector<Channel*> ChannelList;
Epoll(EventLoop* loop);
~Epoll();
void poll(int timeoutMs,ChannelList* activeChannel);//先不返回Timestamp
void updateChannel(Channel* channel);
void removeChannel(Channel* channel);
void assertInLoopThread() { ownerLoop_->assertInLoopThread(); }
private:
static const int kInitEventsize = 20;
void fillActiveChannels(int numEvents,ChannelList* activeChannels) const;
void update(int operation, Channel* channel);
typedef std::vector<struct epoll_event> EventList;
typedef std::map<int,Channel*> ChannelMap;
EventLoop* ownerLoop_;
int epollfd_;
EventList events_;//vector<epoll_event>監聽的事件
ChannelMap channels_;//map<fd,fd所對應的channel*>
};
Epoll類是對epoll處理的抽象類,正確的epoll使用方法是epoll_create -> epoll_ctl -> epoll_wait,分別在Epoll::Epoll(),Epoll::update(),Epoll::poll()中進行處理,這裏對於epoll的原理不再概述。
參考:muduo源碼,Linux多線程服務器編程