涉及的类:EPollPoller EventLoop Channel TimeQueue
其中错误,欢迎指出!
//EventLoop.h
boost::scoped_ptr poller_;
ChannelList activeChannels_; //监听到的事件
首先先了解EPollPoller与Channel,这两个类是有联系的,看代码的时候建议一起看。
- EPollPoller与Channel
首先EPollPoller类是继承了Poller类的,由于Poller类实在太短,所以我就拿了EPollPoller类说。
(epoll:http://blog.csdn.net/ljx0305/article/details/4065058)
成员变量如下:
int epollfd_; //epoll句柄,以后要监听什么事件注册到这里
typedef std::vector<struct epoll_event> EventList;
EventList events_; //监听到的活动的事件,作为epoll_wait参数
typedef std::map<int, Channel*> ChannelMap;
ChannelMap channels_; //描述符与Channel(事件)的映射,这里的事件,不一定是正在监听的事件(因为有可能是之前监听但后来删掉不过仍留在ChannelMap中的映射)
EventLoop* ownerLoop_;//指向EventLoop的指针。
还是直接在代码中注释好了。上面是介绍了EPollPoller类的成员变量,以下是成员方法的注释。
#include
#include
#include
#include
#include
#include
#include
#include
using namespace muduo;
using namespace muduo::net;
// On Linux, the constants of poll(2) and epoll(4)
// are expected to be the same.
BOOST_STATIC_ASSERT(EPOLLIN == POLLIN);
BOOST_STATIC_ASSERT(EPOLLPRI == POLLPRI);
BOOST_STATIC_ASSERT(EPOLLOUT == POLLOUT);
BOOST_STATIC_ASSERT(EPOLLRDHUP == POLLRDHUP);
BOOST_STATIC_ASSERT(EPOLLERR == POLLERR);
BOOST_STATIC_ASSERT(EPOLLHUP == POLLHUP);
namespace
{
/*
这3个const变量是来设置Channel类的index变量。
用来指示Channel事件在EPollPoller的注册情况。
具体分析见该段代码下的列表中。。
*/
const int kNew = -1;
const int kAdded = 1;
const int kDeleted = 2;
}
//构造函数:调用epoll_create1创建了epoll句柄。初始化了其他成员变量。
EPollPoller::EPollPoller(EventLoop* loop)
: Poller(loop),
epollfd_(::epoll_create1(EPOLL_CLOEXEC)),
events_(kInitEventListSize)
{
if (epollfd_ < 0)
{
LOG_SYSFATAL << "EPollPoller::EPollPoller";
}
}
//析构函数:关闭了epollfd
EPollPoller::~EPollPoller()
{
::close(epollfd_);
}
/***poll函数:
poll监听在epollfd上注册的描述符,其中activeChannels变量是EventLoop中传递过来的
(EventLoop类只记载了活跃的事件,注册了什么事件交给了其成员变量poller),通过
fillActiveChannels函数来完成写入到activeChannels。
***/
Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels)
{
LOG_TRACE << "fd total count " << channels_.size();
//调用epoll_wait,活跃的事件则放在events中。
int numEvents = ::epoll_wait(epollfd_,
&*events_.begin(),
static_cast<int>(events_.size()),
timeoutMs);
//保存errno
int savedErrno = errno;
//这里层层调用下去会发现其实就是调用了gettimeofday记录了下时间。
Timestamp now(Timestamp::now());
//对epoll_wait返回值判断。
if (numEvents > 0)//有活跃事件
{
LOG_TRACE << numEvents << " events happended";
/*
将记录了活跃事件成员events塞到activeChannels中,因为poll函数主要
由EventLoop类调用,传递参数,activeChannels是EventLoop类的成员变量。
*/
fillActiveChannels(numEvents, activeChannels);
if (implicit_cast(numEvents) == events_.size())
{
events_.resize(events_.size()*2);
}
}
else if (numEvents == 0) //无活跃事件
{
LOG_TRACE << "nothing happended";
}
else //出错
{
// error happens, log uncommon ones
if (savedErrno != EINTR)
{
errno = savedErrno;
LOG_SYSERR << "EPollPoller::poll()";
}
}
return now;
}
/**fillActiveChannels
创建一个临时channel,用events来设置其中的变量,尤其是是fd,revents,具体与events中变
量的对应见下。应该是类Channel与结构体struct epoll_event events的对应。创建好再将其
push_back到activeChannels中。
**/
void EPollPoller::fillActiveChannels(int numEvents,
ChannelList* activeChannels) const
{
assert(implicit_cast(numEvents) <= events_.size());
for (int i = 0; i < numEvents; ++i)
{
Channel* channel = static_cast(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);
}
}
//注册或修改channel。对照kNew kDeleted kAdded来食用更佳
void EPollPoller::updateChannel(Channel* channel)
{
Poller::assertInLoopThread();
const int index = channel->index();
LOG_TRACE << "fd = " << channel->fd()
<< " events = " << channel->events() << " index = " << index;
if (index == kNew || index == kDeleted) //说明该channel未注册到epollfd中
{
// a new one, add with EPOLL_CTL_ADD
int fd = channel->fd();
if (index == kNew)
{
assert(channels_.find(fd) == channels_.end());
channels_[fd] = channel; //kNew表示std::map channels_也没有,添加进去。
}
else // index == kDeleted
{
assert(channels_.find(fd) != channels_.end());
assert(channels_[fd] == channel);
}
channel->set_index(kAdded);
update(EPOLL_CTL_ADD, channel); //注册到epollfd
}
else //已注册 kAdded。
{
// update existing one with EPOLL_CTL_MOD/DEL
int fd = channel->fd();
(void)fd;
assert(channels_.find(fd) != channels_.end());
assert(channels_[fd] == channel);
assert(index == kAdded);
if (channel->isNoneEvent()) //不监听事件了
{
update(EPOLL_CTL_DEL, channel);//从epollfd中删除
channel->set_index(kDeleted);
}
else //已注册的channel,也许是更改了一些监听事件类型
{
update(EPOLL_CTL_MOD, channel);//修改该事件。
}
}
}
//从epollfd和std::map中都删除channel,代码很简单。喜欢用assert来检查
void EPollPoller::removeChannel(Channel* channel)
{
Poller::assertInLoopThread();
int fd = channel->fd();
LOG_TRACE << "fd = " << fd;
assert(channels_.find(fd) != channels_.end());
assert(channels_[fd] == channel);
assert(channel->isNoneEvent());
int index = channel->index();
assert(index == kAdded || index == kDeleted);
size_t n = channels_.erase(fd);
(void)n;
assert(n == 1);
if (index == kAdded)
{
update(EPOLL_CTL_DEL, channel);
}
channel->set_index(kNew);
}
//封装epoll_ctl。
void EPollPoller::update(int operation, Channel* channel)
{
struct epoll_event event;
bzero(&event, sizeof event);
event.events = channel->events();//这两行代码也能反应channel跟struct epoll_event的映射。
event.data.ptr = channel;
int fd = channel->fd();
LOG_TRACE << "epoll_ctl op = " << operationToString(operation)
<< " fd = " << fd << " event = { " << channel->eventsToString() << " }";
if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)
{
if (operation == EPOLL_CTL_DEL)
{
LOG_SYSERR << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
}
else
{
LOG_SYSFATAL << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
}
}
}
//这个是转出string,没什么好说的。
const char* EPollPoller::operationToString(int op)
{
switch (op)
{
case EPOLL_CTL_ADD:
return "ADD";
case EPOLL_CTL_DEL:
return "DEL";
case EPOLL_CTL_MOD:
return "MOD";
default:
assert(false && "ERROR op");
return "Unknown Operation";
}
}
某个channel的index变量 | EPollPoler类的ChannelMap | 是否注册了epollfd_ |
---|---|---|
kNew | 未存有 | 未监听 |
kDeleted | 存有 | 未监听 |
kAdded | 存有 | 已监听 |
typedef union epoll_data {
void *ptr; //指向channel类变量
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event {
__uint32_t events; //对应channel类中的revent变量
epoll_data_t data; //data.ptr
};
对应的代码:
//在fillActiveChannels函数中的代码体现
Channel* channel = static_cast(events_[i].data.ptr);
channel->set_revents(events_[i].events);
//在update函数中的代码体现
event.events = channel->events();
event.data.ptr = channel;
EPollPoller类和Channel类的交互。是通过Channel类中的index和revent进行交互的。index用来告知EPollPoller注册情况。revent告知Channel啥类型的事件活跃。
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.
bool logHup_;
boost::weak_ptr<void> tie_; //锁?
bool tied_;
bool eventHandling_; //是否正在执行回调函数?
bool addedToLoop_; //是否加入到时间循环?
typedef boost::function<void()> EventCallback;
typedef boost::function<void(Timestamp)> ReadEventCallback;
ReadEventCallback readCallback_;//读回调
EventCallback writeCallback_; //写回调
EventCallback closeCallback_;//关闭回调
EventCallback errorCallback_;//出错回调
然后是介绍成员方法:
//先介绍代码少的内联函数。在channel.h头文件中。
int fd() const { return fd_; }
int events() const { return events_; }
void set_revents(int revt) { revents_ = revt; } // used by pollers
// int revents() const { return revents_; }
bool isNoneEvent() const { return events_ == kNoneEvent; }
//关于kReadEvent|kWriteEvent|kNoneEvent,见channel.cc文件。下面也会列出。
//update函数其实层层深入会发现其实就是调用了EPollPoller类的updateChannel方法。注册或修改。
void enableReading() { events_ |= kReadEvent; update(); }
void disableReading() { events_ &= ~kReadEvent; update(); }
void enableWriting() { events_ |= kWriteEvent; update(); }
void disableWriting() { events_ &= ~kWriteEvent; update(); }
void disableAll() { events_ = kNoneEvent; update(); }
bool isWriting() const { return events_ & kWriteEvent; }
bool isReading() const { return events_ & kReadEvent; }
// for Poller
int index() { return index_; }
void set_index(int idx) { index_ = idx; }
//下面这个是channel.cc文件上的。
#include
#include
#include
#include
#include
using namespace muduo;
using namespace muduo::net;
const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = POLLIN | POLLPRI;
const int Channel::kWriteEvent = POLLOUT;
//构造函数:初始化变量,index赋值为-1,即kNew,表示还未加入epollfd和std::map channels_。
Channel::Channel(EventLoop* loop, int fd__)
: loop_(loop),
fd_(fd__),
events_(0),
revents_(0),
index_(-1),
logHup_(true),
tied_(false),
eventHandling_(false),
addedToLoop_(false)
{
}
Channel::~Channel()
{
assert(!eventHandling_);
assert(!addedToLoop_);
if (loop_->isInLoopThread())
{
assert(!loop_->hasChannel(this));
}
}
void Channel::tie(const boost::shared_ptr<void>& obj)
{
tie_ = obj; //对锁的赋值
tied_ = true; //有锁标志
}
//这里update调用了EventLoop中的updateChannel,其实追根究底还是在调用EPollPoller中updateChannel方法。
void Channel::update()
{
addedToLoop_ = true;
loop_->updateChannel(this);
}
//与update同理,层层调用去看还是在调用EPollPoller中removeChannel方法
void Channel::remove()
{
assert(isNoneEvent());
addedToLoop_ = false;
loop_->removeChannel(this);
}
/*
handleEvent:
作用:供EventLoop类调用,因为EventLoop类只会发现你这个channel有事件
,至于是什么事件需要channel类自己查看revent变量(EPollPoller传递的),
然后根据其值来调用相关的事件回调函数。(这也是handleEventWithGuard的逻辑)
逻辑:判断这个channel是否有锁,一般该事件涉及多事件调用才需要用到锁。
若有锁,则获得锁后调用handleEventWithGuard
若无锁,直接调用handleEventWithGuard。
*/
void Channel::handleEvent(Timestamp receiveTime)
{
boost::shared_ptr<void> guard;
if (tied_) //判断是否有锁。有锁。
{
guard = tie_.lock();//锁上!
if (guard) //是否锁上了。
{
handleEventWithGuard(receiveTime);
}
}
else
{
handleEventWithGuard(receiveTime);
}
}
/*
handleEventWithGuard:
根据revent值来调用相关的事件回调函数。
man 2 poll 可见每个宏定义POLLXXX的含义。
*/
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) //fd未打开。。。见man 2 poll
{
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;
}
//event事件类型转字符串
string Channel::reventsToString() const
{
return eventsToString(fd_, revents_);
}
string Channel::eventsToString() const
{
return eventsToString(fd_, events_);
}
//
string Channel::eventsToString(int fd, int ev)
{
std::ostringstream oss;
oss << fd << ": ";
if (ev & POLLIN)
oss << "IN ";
if (ev & POLLPRI)
oss << "PRI ";
if (ev & POLLOUT)
oss << "OUT ";
if (ev & POLLHUP)
oss << "HUP ";
if (ev & POLLRDHUP)
oss << "RDHUP ";
if (ev & POLLERR)
oss << "ERR ";
if (ev & POLLNVAL)
oss << "NVAL ";
return oss.str().c_str();
}
Channel跟EPollPoller逻辑一般是这样:
创建个channel,比如设置读事件,先设置回调函数,然后enableReading(),这里就非常关键了,它会先设置events变量的事件类型,然后update,这个update会让该事件所在loop来调用loop上的EPollPoller的updateChannel。具体代码如下:
//Channel.cc
void Channel::update()
{
addedToLoop_ = true;
loop_->updateChannel(this);
}
//EventLoop.cc
void EventLoop::updateChannel(Channel* channel)
{
assert(channel->ownerLoop() == this);
assertInLoopThread();
poller_->updateChannel(channel);
}
以上是设置事件并注册到epollfd中的过程。删除事件,以及channel.h中的disableXXX同理。
而监听到事件的代码也是非常清晰的。首先在EventLoop的loop函数中监听到。然后装进activeChannels_,再由channel类调用本身的handleEvent。
//EventLoop.cc
loop(……){
……
pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
……
for (ChannelList::iterator it = activeChannels_.begin();
it != activeChannels_.end(); ++it)
{
currentActiveChannel_ = *it;
currentActiveChannel_->handleEvent(pollReturnTime_);
}
……
}
Channel一般为读写事件。
1、一般有这个场景,当前有个EventLoop loopA在时间循环中。
2、我们这时候有个读事件想在loopA中监听,好,先创建一个channel,弄好描述符,还有loop_也指向loopA,设置好感兴趣的事件类型,还有回调函数。其中index为kNew,用来等会告诉EPollPoller说,这个channel注册情况是一片空白,既不在epollfd上监听,也没再那个map上。然后enableXXX,里面就会update一下,调用了loopA中的updatechannel方法。
3、loopA一看,什么?要我帮你update,这里可能是注册,也可能是del啥的,具体干嘛他不知道,但他知道他的手下—-成员变量poller_是干这类事情的,他会吩咐poller说,这事情交给你去办。而channel交给loopA时候把“登记材料”(index、events)也准备好了,所以在epollfd上登记这个读事件就会在EPollPoller上实际进行。
4、登记完后,过了会,读事件来了,这时候最先知道这件事情的是epollfd,于是他把这事情告诉了loopA手下activeChannels,让他转告loopA,让他拿主意,loopA听到后,就吩咐这事件所属的channel说,你们的读事件来了,快去解决一下。
5、然后channel就检测revent,是epollfd上检测出的事件的类型,然后再调用相应的读或写或出错回调函数。
——————————————————————————————————————————————————————
写到这里,有点倦了
就把整个大致说一遍,以后要是忘了还能找回其中的逻辑。真要写完写清楚对每条代码都解释清楚,要写好久。
1、定时事件muduo也是使用了描述符,用TimerQueue类来描述,在构造这个定时事件的时候封装成channel,让poll来监听其读事件。(对TimerQueue还不算太清楚)
2、EventLoop自身有个唤醒自身线程的描述符,在创建的时候就注册到了epollfd上。
void EventLoop::runInLoop(const Functor& cb)
{
if (isInLoopThread())
{
cb();
}
else
{
queueInLoop(cb);
}
}
void EventLoop::queueInLoop(const Functor& cb)
{
{
MutexLockGuard lock(mutex_);
pendingFunctors_.push_back(cb);
}
if (!isInLoopThread() || callingPendingFunctors_)
{
wakeup();//唤醒。往唤醒描述符上写……
}
}
runInLoop函数会催着loop所在线程去执行cb函数。
3、还有一类事件就是channel。在上面说的也算详细了。