Muduo库源码剖析(一)——Channel
Muduo库源码剖析(一)——Channel
说明
本源码剖析是在muduo基础上,保留关键部分进行改写分析。
要点总结
事件分发器 event dispatcher
中最重要的两个类型 channel
和 Poller
Channel
可理解为通道,poller
往通道传输数据(事件发生情况)。
EventLoop包含多个channel 和一个 Poller;
Channel相当于是对socket的事件处理封装,包含了socket的详细信息,scoket以及感兴趣的事件都在channel里;
channel是muduo库负责注册读写事件的类,并保存了fd读写事件发生时调用的回调函数,如果poll/epoll有读写事件发生则将这些事件添加到对应的通道中。
-
一个
channel
对应唯一EventLoop
,一个EventLoop
可以有多个channel
。 -
Channel类不负责fd的生存期,fd的生存期是由socket决定的,断开连接关闭描述符。
-
当有fd返回读写事件时,调用提前注册的回调函数处理读写事件
-
头文件中只给类的前置声明,而在源文件中再给出头文件包含,因为源文件会被编程动态库.so, 减少对外暴露
-
weak_ptr
用于观察绑定对象的状态,并且可以尝试提升为shared_ptr
Channel这个模块对应Reactor模型上的 Demultiplex (多路复用器)
重点代码详解
// Channel.h
#pragma once#include "noncopyable.h"
#include "Timestamp.h"#include <functional>
#include <memory>// 头文件中只给类的前置声明,而在源文件中再给出头文件包含
// 因为源文件会被编程动态库.so, 减少对外暴露
class EventLoop;class Channel : noncopyable
{
public:using EventCallback = std::function<void()>;using ReadEventCallback = std::function<void(Timestamp)>;Channel(EventLoop *loop, int fd);~Channel();// fd得到poller通知后调用其处理事件void handleEvent(Timestamp recevieTime);void setReadCallback(ReadEventCallback cb) { readCallback_ = std::move(cb); }void setWriteCallback(EventCallback cb) { writeCallback_ = std::move(cb); }void setCloseCallback(EventCallback cb) { closeCallback_ = std::move(cb); }void setErrorCallback(EventCallback cb) { errorCallback_ = std::move(cb); }// 防止当Channel的所有者被手动remove掉时,Channel 仍在执行回调void tie(const std::shared_ptr<void>&); // 检测资源存活状态int fd() const { return fd_; }int events() const { return events_; }void set_revents(int revt) { revents_ = revt; }// 设置fd相应的事件状态// enableReading 让fd对读事件感兴趣// update()底层也是调用 epoll_ctlvoid enableReading() { events_ |= kReadEvent; update(); }void disableReading() { events_ &= ~kReadEvent; update(); }void enableWriting() { events_ |= kWriteEvent; update(); }void disableWriting() { events_ &= ~kWriteEvent; update(); }void disableAll() { events_ = kNoneEvent; }int index() { return index_; }void set_index(int idx) { index_ = idx; }// oneloop per thread// 当前Channel所属的eventloopEventLoop* ownerLoop() {return loop_;}void remove();private:void update();void handleEventWithGuard(Timestamp recvTime);static const int kNoneEvent; // 感兴趣的事件类型,该变量表示不感兴趣任何事件static const int kReadEvent; static const int kWriteEvent; EventLoop *loop_; // 事件循环const int fd_; //fd, poller监听的对象int events_; // 注册感兴趣的事件int revents_; // poller返回的具体发生的事件类型(可读?可写?)int index_;std::weak_ptr<void> tie_; // 用于观察shared_ptr的状态bool tied_;// 因为Channel里能得知fd最终发生的具体事件revents_// 故它负责调用对应的回调ReadEventCallback readCallback_;EventCallback writeCallback_;EventCallback closeCallback_;EventCallback errorCallback_;};
// Channel.cpp
#include "Channel.h"
//#include "EventLoop.h"
#include "Logger.h"#include <sys/epoll.h>const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = EPOLLIN | EPOLLPRI;
const int Channel::kWriteEvent = EPOLLOUT;Channel::Channel(EventLoop *loop, int fd): loop_(loop), fd_(fd), events_(0), revents_(0), index_(-1), tied_(false)
{
}Channel::~Channel()
{
}// ??channel的tie方法什么时候调用过
void Channel::tie(const std::shared_ptr<void> &obj)
{tie_ = obj;tied_ = true;
}// 当改变Channel所表示fd的events事件后,update负责在poller里更改fd相应事件--epoll_ctl
// EventLoop has ChannelList Poller
void Channel::update()
{// 通过Channel所属的EventLoop,调用Poller的相应方法,注册fd的events事件// [TODO]// loop_->updateChannel(this);
}// 在Channel所属的EventLoop中,把当前Channel删除
void Channel::remove()
{// [TODO]// loop_->removeChannel(this);
}// fd得到poller通知后处理事件
void Channel::handleEvent(Timestamp receiveTime)
{if(tied_) // 资源存活{std::shared_ptr<void> guard = tie_.lock();if(guard){handleEventWithGuard(receiveTime);}}else {handleEventWithGuard(receiveTime);}
}// 根据Poller通知的Channel发生的具体事件,调用相应的回调
void Channel::handleEventWithGuard(Timestamp receiveTime)
{LOG_INFO("channel handleEvent revents:%d\\n", revents_);// EPOLLHUP 表示读写都关闭if((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN)){if(closeCallback_){closeCallback_();}}if(revents_ & EPOLLERR){if(errorCallback_){errorCallback_();}}if(revents_ & (EPOLLIN | EPOLLPRI)){if(readCallback_){readCallback_(receiveTime);}}if(revents_ & EPOLLOUT){if(writeCallback_){writeCallback_();}}}
参考资料
-
陈硕. 《LInux多线程服务端编程》
-
施磊. 《手写C++Muduo网络库》