这两个类主要是对IO模型Epoll和Poll的一个封装,功能是调用Epoll和Poll相关系统函数,注册删除更新IO文件描述符,返回活动的通道。
Poller.h
protected:
typedef std::map<int, Channel*> ChannelMap;
ChannelMap channels_; // fd,Channel 集合
private:
EventLoop* ownerLoop_;
基类实现了hasChannel函数,判断Map是否拥有此Channel
基类持有Loop和Map
PollPoller
typedef std::vector<struct pollfd> PollFdList; //文件描述符集合
PollFdList pollfds_;
Timestamp PollPoller::poll(int timeoutMs, ChannelList* activeChannels) //Poll具体实现
{
// XXX pollfds_ shouldn't change
int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs); //poll系统调用,阻塞在这里等待IO数据到来
int savedErrno = errno;
Timestamp now(Timestamp::now());
if (numEvents > 0)
{
LOG_TRACE << numEvents << " events happened";
fillActiveChannels(numEvents, activeChannels); //根据事件个数填写活动通道
}
.....
}
void PollPoller::fillActiveChannels(int numEvents,
ChannelList* activeChannels) const
{
//遍历整个文件描述符数组,找到标志位(revents)被更改的描述符
for (PollFdList::const_iterator pfd = pollfds_.begin();
pfd != pollfds_.end() && numEvents > 0; ++pfd)
{
if (pfd->revents > 0)
{
--numEvents; //活动个数减1控制循环
ChannelMap::const_iterator ch = channels_.find(pfd->fd); //根据文件描述符fd索引到channel
assert(ch != channels_.end()); //断言是否是channel集合末尾(ch为空)
Channel* channel = ch->second; //获得其value值
assert(channel->fd() == pfd->fd); //如果fd<0则表示该channel暂停工作
channel->set_revents(pfd->revents); //channel设置事件状态
// pfd->revents = 0;
activeChannels->push_back(channel);
}
}
}
//更新channel
void PollPoller::updateChannel(Channel* channel)
{
Poller::assertInLoopThread(); //断言是Poller是在本线程工作
LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();
if (channel->index() < 0) //index创建时默认-1表示新的channel
{
// a new one, add to pollfds_
assert(channels_.find(channel->fd()) == channels_.end()); //断言该fd不是已有channel
struct pollfd pfd; //添加poll文件描述符
pfd.fd = channel->fd();
pfd.events = static_cast<short>(channel->events()); //添加感兴趣的事件
pfd.revents = 0;
pollfds_.push_back(pfd);
int idx = static_cast<int>(pollfds_.size())-1; //将文件描述符下标返回
channel->set_index(idx);
channels_[pfd.fd] = channel;
}
else
{
// update existing one
assert(channels_.find(channel->fd()) != channels_.end()); //断言此fd是存在的
assert(channels_[channel->fd()] == channel);
int idx = channel->index();
assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));
struct pollfd& pfd = pollfds_[idx];
assert(pfd.fd == channel->fd() || pfd.fd == -channel->fd()-1);
pfd.fd = channel->fd();
pfd.events = static_cast<short>(channel->events());
pfd.revents = 0;
if (channel->isNoneEvent()) //设置channel为忽略事件时fd取反-1
{
// ignore this pollfd
pfd.fd = -channel->fd()-1; //作用是 fd为0时,取反还是0,因此-1
}
}
}
void PollPoller::removeChannel(Channel* channel)
{
Poller::assertInLoopThread();
LOG_TRACE << "fd = " << channel->fd();
assert(channels_.find(channel->fd()) != channels_.end());
assert(channels_[channel->fd()] == channel);
assert(channel->isNoneEvent()); //注意要先设置为忽略事件
int idx = channel->index();
assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));
const struct pollfd& pfd = pollfds_[idx]; (void)pfd;
assert(pfd.fd == -channel->fd()-1 && pfd.events == channel->events());
size_t n = channels_.erase(channel->fd());
assert(n == 1); (void)n;
if (implicit_cast<size_t>(idx) == pollfds_.size()-1) //如果删除的通道不在最后一个元素,则调换数组两个元素
{
pollfds_.pop_back();
}
else
{
int channelAtEnd = pollfds_.back().fd;
iter_swap(pollfds_.begin()+idx, pollfds_.end()-1);
if (channelAtEnd < 0) //最后一个元素小于0则需要改变为正值再找到对应channel设置下标
{
channelAtEnd = -channelAtEnd-1;
}
channels_[channelAtEnd]->set_index(idx);
pollfds_.pop_back();
}
}
EpollPoller
typedef std::vector<struct epoll_event> EventList;
int epollfd_;
EventList events_;
void EPollPoller::updateChannel(Channel* channel)
{
Poller::assertInLoopThread();
const int index = channel->index(); //获得当前IO Channel状态
LOG_TRACE << "fd = " << channel->fd()
<< " events = " << channel->events() << " index = " << index;
if (index == kNew || index == kDeleted) //kNew是表示当前IO Channel是新的,并且需要添加进epoll IO文件描述符中
//kDeleted表示IO Channel之前存在,现在需要添加进epoll IO文件描述符中
{
// a new one, add with EPOLL_CTL_ADD
int fd = channel->fd();
if (index == kNew)
{
assert(channels_.find(fd) == channels_.end());
channels_[fd] = channel;
}
else // index == kDeleted
{
assert(channels_.find(fd) != channels_.end());
assert(channels_[fd] == channel);
}
channel->set_index(kAdded); //统一设置为已经添加进epoll的状态
update(EPOLL_CTL_ADD, channel); //对update设置add的操作
}
else
{
// 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()) //忽略channel 所在epoll中的文件描述符
{
update(EPOLL_CTL_DEL, channel);
channel->set_index(kDeleted); //当前channel状态设置为已删除
}
else
{
update(EPOLL_CTL_MOD, channel); //更新channel的感兴趣事件
}
}
}
Epoll和Poll差别就在于update上面,主要是Epoll的index和operation一一对应,而Poll的index是数组下标