时间有限,net篇采用边看代码边写。
net部分的类都定义在muduo::net命名空间下。
EventLoop类
这个类涉及好几个类:
boost::scoped_ptr<Poller> poller_;
boost::scoped_ptr<TimerQueue> timerQueue_;
boost::scoped_ptr<Channel> wakeupChannel_;
ChannelList activeChannels_;
Channel* currentActiveChannel_;
EventLoop类有其类对于操作的API,这里不讲,等到阅读到对应代码时再具体分析。
首先EventLoop类构造函数中有:
wakeupChannel_->setReadCallback(
boost::bind(&EventLoop::handleRead, this));
wakeupChannel_->enableReading();
这个肯定是注册epoll或poll监听的事件,事件到达就可唤醒当前线程。
事件对象wakeupFd_实际为:
evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
read evtfd会读取8字节值, 并把计数器重设为0
EventLoop类还有一些状态成员,用来同步操作,不赘述。
操作流程大致为:
当前线程创建EventLoop对象,假设为loop。
之后可以设置监听事件,可以注册定时器函数,也可以注册到pendingFunctors_,因为loop在一个线程中是唯一的,只能创建一个。所以这些操作需要在调用loop前完成,主逻辑在loop函数中且是循环执行。
跨线程可以loop.runInLoop(…)把调度函数放到pendingFunctors_队列中,并唤醒事件,也就是向wakeupFd_写操作——计数器加1;可以run…() 注册定时器事件;也可以quit停止loop;其余操作目前看来不可以。
Poller类
Poller类是PollPoller类和EPollPoller类的父类,成员数据:
protected:
typedef std::map<int, Channel*> ChannelMap;
ChannelMap channels_;
private:
EventLoop* ownerLoop_;
由此可见Poller是和EventLoop关联的,虚函数如下:
virtual ~Poller();
virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;
virtual void updateChannel(Channel* channel) = 0;
virtual void removeChannel(Channel* channel) = 0;
virtual bool hasChannel(Channel* channel) const;
其中hasChannel()已在Poller类中实现
Poller类中newDefaultPoller函数会生成一个默认对象,此函数单独定义在一个文件中。
在阅读代码时,可结合I/O 多路复用技术看。
PollPoller类和EPollPoller类除了本身封装linux API外,最重要的是对channels_的操作。
这是个Map,保存监听对象的fd与Channel对应关系
Channel类
事件分为无、读、写三种:
const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = POLLIN | POLLPRI;
const int Channel::kWriteEvent = POLLOUT;
Channel类中重要的成员数据有:
EventLoop* loop_;
const int fd_;
int events_;
int revents_; // it's the received event types of epoll or poll
int index_; // used by Poller.
成员函数handleEvent完成事件处理,函数原型:
void handleEvent(Timestamp receiveTime);
小结
上面主要讲了三个东西:
EventLoop:事件循环——我把发生的事件一一处理
Poller:I/O多路复用——我来监督事件有没有发生
Channel:事件——我来决定I/O事件,并告诉你发生后该干什么
从最基本谈起:
1、要创建Poller对象
而创建Poller对象的构造函数表示需要告知隶属于哪个事件循环,于是需要创建EventLoop对象
创建EventLoop对象意味着要创建其成员Poller对象和Channel对象
2、需要注册事件,即调用Poller.updateChannel(Channel* channel)
而这必须先创建Channel对象
3、等待事件发生,即调用Poller.poll(int timeoutMs, ChannelList* activeChannels)
4、事件处理,即调用Channel.handleEvent(Timestamp receiveTime)
5、循环3、4
上述的3、4都是在EventLoop.loop()中完成的,这意味着EventLoop.poller_即是1中想要创建的对象。
于是实际情况就变成了:
1、创建EventLoop对象
2、创建Channel对象
3、调用EventLoop.updateChannel()注册事件
4、调用EventLoop.loop()
还有两个小问题
1、EventLoop.wakeupChannel_是如何注册的?
在构造函数时即完成了。实际上wakeupChannel_(evtfd的读事件)绕了一大圈
wakeupChannel_.enableReading()
wakeupChannel_.update()
loop_->updateChannel(this);
又回来了完成注册。
2、既然EventLoop.loop()是循环,且EventLoop对象一个线程中只能创建一个,事件状态变更又必须是当前线程,如何做到变更或停止呢?
我认为只能由其他线程quit()停止之后再完成更新。待验证。