EPollPoller的主要实现:作为poller的派生类,把基类给派生类保留的这些纯虚函数的接口实现出来。
overide表示在派生类里面,这些方法是覆盖方法。必须由编译器来保证在基类里面一定有这些函数的接口的声明。在派生类要重写他们。
给EPollPoller的析构函数写overide,就是让编译器给你检查基类的析构一定是虚函数。
底层是vector,放eventlist,可以动态地扩容。
成员变量的epollfd要通过epoll_create来创建,映射的就是epoll底层的文件系统
epoll_wait第二个参数放的是最终发生事件的fd,返回值就是发生事件的fd的数量。
EPollPoller.h
#pragma once
#include "Poller.h"
#include "Timestamp.h"
#include<vector>
#include<sys/epoll.h>
class Channel;
/*
epoll使用:
1. epoll_create创建fd
2. epoll_ctl添加想让epoll监听的fd以及针对fd所感兴趣的事件,进行add/mod/del
3. epoll_wait
*/
class EPollPoller : public Poller
{
public:
//表示epoll_create行为
EPollPoller(EventLoop* loop);
~EPollPoller() override;
//重写基类Poller的抽象方法
//表示epoll_wait行为
Timestamp poll(int timeoutMs, ChannelList *activeChannels);
//下面两个方法表示epoll_ctl的行为
void updateChannel(Channel* channel);
void removeChannel(Channel* channel);
private:
static const int kInitEventListSize = 16;//初始化vector长度
using EventList = std::vector<epoll_event>;
//填写活跃的连接
void fillActiveChannels(int numEvents,ChannelList *activeChannels) const;
//更新Channel通道
void update(int operation,Channel *channel);
int epollfd_;
EventList events_;//epoll_wait的第二个参数
};
epoll_create的参数size没有意义了,但是必须是大于0的数
epoll_create1,当我们去使用epoll_create1的时候,创建的epollfd,然后在当前线程里面再去fork创建一个子进程,然后用exec替换子进程,在子进程里面就把父进程设置成标志的fd,资源就都给关闭了。
结构体的fd就是epoll要监听的事件,ptr就是针对fd携带的数据,相当于是fd对于的channel
EpollPoller.cc
#include "EPollPoller.h"
#include "Logger.h"
#include<errno.h>
#include <unistd.h>
#include<strings.h>
//channel的成员变量index_表示在Poller/EpollPoller中的状态
const int kNew = -1; // 一个Channel还没有添加到Poller里面,channel的成员index_ = -1
const int kAdded = 1; //一个Channel已经添加到Poller里面
const int kDeleted = 2; // 一个Channel已经从epoll红黑树中删除了,但是还存在于poller的map中
EPollPoller::EPollPoller(EventLoop* loop)
:Poller(loop)
,epollfd_(::epoll_create1(EPOLL_CLOEXEC))
,events_(kInitEventListSize) //vector<epoll_event>,默认长度16
{
if (epollfd_ < 0)
{
LOG_FATAL("epoll_create error:%d \n", errno);
}
}
EPollPoller::~EPollPoller()
{
::close(epollfd_);
}
//eventloop会创建一个channellist,并把创建好的channellist的地址传给poll
//poll通过epoll_wait监听到哪些fd发生了事件,把真真正正发生事件的channel通过形参发送到eventloop提供的实参里面
Timestamp EPollPoller::poll(int timeoutMs, ChannelList *activeChannels)
{
//实际上应该用LOG_DEBUG输出日志更合理,可以设置开启或者不开启
LOG_INFO("func=%s fd total count:%ld\n",__FUNCTION__,channels_.size());
//对poll的执行效率有所影响
int numEvents = ::epoll_wait(epollfd_,&*events_.begin(),static_cast<int>(events_.size()),timeoutMs);
//events_.begin()返回首元素的迭代器(数组),也就是首元素的地址,是面向对象的,要解引用,就是首元素的值,然后取地址
//就是vector底层数组的起始地址 static_cast类型安全的转换 timeoutMs超时时间
int saveErrno = errno;//全局的变量errno,库里的,poll可能在多个线程eventloop被调用 ,所以有局部变量存起来
Timestamp now(Timestamp::now());//获取当前时间
if(numEvents > 0) //表示有已经发生相应事件的个数
{
LOG_INFO("%d events happened \n",numEvents);
fillActiveChannels(numEvents,activeChannels);
if(numEvents == events_.size())
{
events_.resize(events_.size() * 2); //所有的监听的event都发生事件了,得扩容了
}
}
else if(numEvents == 0) //epoll_wait这一轮监听没有事件发生,timeout超时了
{
LOG_DEBUG("%d timeout! \n",__FUNCTION__);
}
else
{
if(saveErrno != EINTR) //不等于外部的中断 ,是由其他错误类型引起的
{
errno = saveErrno; //适配 ,把errno重置成当前loop之前发生的错误的值
LOG_ERROR("EPollPoller::poll() err!");
}
}
return now;
}
//下面两个方法表示epoll_ctl的行为
//channel update remove => Eventloop updateChannel removeChannel => Poller updateChannel removeChannel
/**
* EventLoop => poller.poll
* ChannelList Poller
* ChannelMap <fd, channel*> epollfd
* EventLoop里面有一个ChannelList,就是所有的Channel都是在EventLoop里面管理的,
* Channel创建以后,向Poller里面注册的和未注册过的全部放在EventLoop里面的ChannelList,
* 如果某些Channel向Poller里面注册过了,那么这些Channel会写到Poller里面的成员变量ChannelMap里面,
* poll方法还是通过EventLoop来调用Poller.poll
* poll函数的作用是通过epoll_wait监听到哪些fd发生事件,也就是哪些channel发生事件,
* 把真正发生事件的channel通过形参填到EventLoop提供的实参里面
*/
void EPollPoller::updateChannel(Channel* channel)
{
const int index = channel->index();
LOG_INFO("func=%s => fd=%d events=%d index=%d\n",__FUNCTION__,channel->fd(),channel->events(),index);
if(index == kNew || index == kDeleted) //未添加或者已删除
{
//注意:kDeleted状态表示从epoll红黑树上删除,但是还存在于map表中
if(index == kNew) //未添加,键值对写入map中
{
int fd = channel->fd();
channels_[fd] = channel;
}
channel->set_index(kAdded);
update(EPOLL_CTL_ADD,channel); //相当于调用epoll_ctl,添加1个channel到epoll中
}
else //channel已经在poller上注册过了
{
int fd = channel->fd();
if(channels_[fd]->isNoneEvent()) //已经对任何事件不感兴趣,不需要poller帮忙监听了
{
//只是从红黑树上删除了,还存在于map表中
channel->set_index(kDeleted);
update(EPOLL_CTL_DEL,channel);
}
else
{
update(EPOLL_CTL_MOD,channel); //包含了fd的事件,感兴趣
}
}
}
//从Poller中删除Channel
void EPollPoller::removeChannel(Channel* channel)
{
int fd = channel->fd();
channels_.erase(fd); //从map中删掉
LOG_INFO("func=%s => fd=%d\n",__FUNCTION__,channel->fd());
int index = channel->index();
if(index == kAdded) //如果已注册过
{
update(EPOLL_CTL_DEL,channel); //通过epoll_ctl 删掉
}
channel->set_index(kNew);//设置成未添加的状态
}
void EPollPoller::fillActiveChannels(int numEvents,ChannelList *activeChannels) const
{
for(int i = 0; i < numEvents; ++i)
{
Channel *channel = static_cast<Channel*>(events_[i].data.ptr);
channel->set_events(events_[i].events);
activeChannels->push_back(channel);//EventLoop就拿到了它的poller给它返回的所有发生事件的channel列表了
//至于EventLoop拿到这些channel干什么事情,我们看 EventLoop的代码
}
}
//更新Channel通道,就是epoll_ctl 的 add/mod/del 操作
void EPollPoller::update(int operation,Channel *channel)
{
epoll_event event;
bzero(&event,sizeof event);
int fd = channel->fd();
event.events = channel->events(); //返回的就是fd所感兴趣的事件
event.data.fd = fd;
event.data.ptr = channel;//绑定的参数
if(::epoll_ctl(epollfd_,operation,fd,&event) < 0) //把fd相关事件更改
{
if(operation == EPOLL_CTL_DEL) //没有删掉
{
LOG_ERROR("epoll_ctl delete error:%d\n",errno);
}
else //添加或者更改错误,这个会自动exit
{
LOG_FATAL("epoll_ctl add/mod error:%d\n",errno);
}
}
}