文章目录
- 一、EPollPoller.h
- 二、EPollPoller::updateChannel
- 三、EPollPoller::update
- 四、EPollPoller::removeChannel
- 五、EPollPoller::poll
- 六、EPollPoller.cc
一、EPollPoller.h
EPollPoller的主要功能是实现Poller类保留的纯虚函数接口poll(epoll_wait)、updateChannel(epoll_ctl)、removeChannel(epoll_ctl)
muduo中EPollPoller.h
如下:
class EPollPoller : public Poller
{
public:
EPollPoller(EventLoop* loop);
~EPollPoller() override;
Timestamp poll(int timeoutMs, ChannelList* activeChannels) override;
void updateChannel(Channel* channel) override;
void removeChannel(Channel* channel) override;
private:
static const int kInitEventListSize = 16;
static const char* operationToString(int op);
void fillActiveChannels(int numEvents, ChannelList* activeChannels) const;
void update(int operation, Channel* channel);
typedef std::vector<struct epoll_event> EventList; // EventList底层是vector,放epoll_event,可以动态地扩容
int epollfd_; // 成员变量的epollfd要通过epoll_create来创建,映射的就是epoll底层的红黑树文件系统
EventList events_;
};
overirde表示在派生类里面,这些方法是覆盖方法,必须由编译器来保证在基类里面一定有这些函数的接口的声明。派生类要重写他们。
给EPollPoller的析构函数写overide,就是让编译器给你检查基类的析构函数一定是虚函数
成员变量的epollfd_要通过epoll_create来创建,映射的就是epoll底层的红黑树文件系统,即内核事件表
重写EPollPoller.h
#pragma once
#include "Poller.h"
#include "Timestamp.h"
#include <vector>
#include <sys/epoll.h>
class Channel;
// 继承Poller的时候需要知道Poller的实际结构,而不仅仅是一个类型声明
class EPollPoller : public Poller{
public:
EPollPoller(EventLoop* loop);
~EPollPoller() override;
// epoll_wait
Timestamp poll(int timeoutMs, ChannelList* activeChannels) override;
// epoll_ctl 更新感兴趣的事件 EPOLL_CTL_ADD EPOLL_CTL_MOD
void updateChannel(Channel* channel) override;
// eventloop中删除channel(fd) EPOLL_CTL_DEL
void removeChannel(Channel* channel) override;
private:
static const int kInitEventListSize = 16;
// 填写活跃的连接,被poll方法调用
void fillActiveChannels(int numEvents, ChannelList* activeChannels) const;
// 更新channel,被updateChannel()和removeChannel()调用
void update(int operation, Channel* channel);
using EventList = std::vector<epoll_event>;
int epollfd_; // epoll_create创建,对应内核时间表
EventList events_; // 存放epoll_event事件的vector
};
二、EPollPoller::updateChannel
标识Channel状态的三个变量
const int kNew = -1; // Channel从来没有添加到epoll或者从epoll和Poller中的map都删除 Channel的成员index_初始化-1
const int kAdded = 1; // Channel已经添加到了epoll和Poller中的map
const int kDeleted = 2; // Channel从epoll红黑树中删除了,但是还存在于Poller的map中
Linux2.6.8之后epoll_create的参数size就没有意义了,但是必须是大于0的数
当我们使用epoll_create1的时候,传入EPOLL_CLOEXEC,创建的epollfd,然后在当前线程里面再去fork创建一个子进程,然后用exec替换子进程(默认子进程继承了父进程所有的fd),在子进程里面就把父进程的fd都关闭,使得fd的引用计数减1。
muduo updateChannel 源码
void EPollPoller::updateChannel(Channel* channel)
{
Poller::assertInLoopThread();
const int index = channel->index(); // 获取channel的状态,kNew、kAdded、kDeleted
LOG_TRACE << "fd = " << channel->fd()
<< " events = " << channel->events() << " index = " << index;
if (index == kNew || index == kDeleted)
{
// a new one, add with EPOLL_CTL_ADD
int fd = channel->fd();
if (index == kNew)
{
assert(channels_.find(fd) == channels_.end());
channels_[fd] = channel; // 如果从未添加到EPollPoller中,那么将fd和对应channel对象添加到Poller中的成员map中
}
else // index == kDeleted
{
// 如果这个channel在Poller中已经被删除了,需要确保fd还在EventLoop,而且可以重新添加到Poller
assert(channels_.find(fd) != channels_.end());
assert(channels_[fd] == channel);
}
channel->set_index(kAdded); // 设置channel状态为kAdded
update(EPOLL_CTL_ADD, channel); // 往Poller上注册channel,就是调用epoll_ctl往epoll红黑树中添加fd
}
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对任何事件都不感兴趣
update(EPOLL_CTL_DEL, channel); // 从当前Poller中删除当前channel,相当于从epoll红黑树中删除fd
channel->set_index(kDeleted);
}
else
{
update(EPOLL_CTL_MOD, channel); // 如果channel已经存在于Poller,并且channel还对事件感兴趣,就是更新epoll红黑树上fd对应的事件
}
}
}
三、EPollPoller::update
调用epoll_ctl,EPOLL_CTL_ADD,EPOLL_CTL_DEL,EPOLL_CTL_MOD
muduo update源码
// operation 就是 EPOLL_CTL_ADD,EPOLL_CTL_DEL,EPOLL_CTL_MOD
void EPollPoller::update(int operation, Channel* channel)
{
struct epoll_event event;
memZero(&event, sizeof event);
event.events = channel->events();
event.data.ptr = channel; // channel挂在epoll_event.epoll_data.ptr
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;
}
}
}
重写update
// epoll_ctl EPOLL_CTL_ADD,EPOLL_CTL_DEL,EPOLL_CTL_MOD 更新channel
void EPollPoller::update(int operation, Channel* channel){
epoll_event event;
memset(&event, 0, sizeof(event));
int fd = channel->fd();
event.events = channel->events();
event.data.fd = fd;
event.data.ptr = channel;
if(::epoll_ctl(epollfd_, operation, fd, &event) < 0){
// epoll_ctl返回值小于0,则出错
if(operation == EPOLL_CTL_DEL){
LOG_ERROR("epoll_ctl delete error:%d\n", errno);
}else{
LOG_FATAL("epoll_ctl add / modify error:%d\n", errno);
}
}
}
四、EPollPoller::removeChannel
removeChannel源码
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);
}
重写removeChannel
// eventloop中删除channel(fd) epoll_ctl EPOLL_CTL_DEL
void EPollPoller::removeChannel(Channel* channel){
int fd = channel->fd();
channels_.erase(fd); // 先在Poller里的map中删除
LOG_INFO("func=%s => fd=%d \n", __FUNCTION__, fd);
int index = channel->index();
if(index == kAdded){
update(EPOLL_CTL_DEL, channel); // 从epoll红黑树中删除
}
channel->set_index(kNew);
}
五、EPollPoller::poll
调用epoll_wait,把发生事件的fd以及对应的Channel添加到activeChannels中
EventLoop创建ChannelList,并把ChannelList传到EPollPoller::poll,poll会把发生事件的channel通过activeChannels填到EventLoop的成员变量ChannelList中
// EventLoop创建ChannelList,并把ChannelList传到EPollPoller::poll,poll会把发生事件的channel通过activeChannels填到EventLoop的成员变量ChannelList中
Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels){
// 实际上应该用LOG_DEBUG更合适
LOG_INFO("func=%s => fd total count : %d \n", __FUNCTION__, channels_.size());
// events_.begin()返回首元素的迭代器,也就是首元素的地址,是面向对象的,解引用后就是首元素的值,然后取地址就得到了vector封装的底层数组的首地址
// epoll_wait返回后,events_底层的数组的前numEvents元素就是所有发生事件的epoll_event
int numEvents = ::epoll_wait(epollfd_, &(*events_.begin()), static_cast<int>(events_.size()), timeoutMs);
// 全局变量errno,poll可能在多个线程中的eventloop被调用,被读写,所以先用局部变量存起来
int saveErrno = errno;
Timestamp now(Timestamp::now());
if(numEvents > 0){
LOG_INFO("%d events happened \n", numEvents);
fillActiveChannels(numEvents, activeChannels); // 这里每次传入的都是一个空vector,把发生事件的Channel添加到EventLoop的成员变量ChannelList中
if(numEvents == (int)events_.size()){
// 发生事件的fd和成员变量EventList的长度相同,需要扩容
events_.resize(events_.size() * 2);
}
}else if(numEvents == 0){
// 没有事件发生,超时
LOG_DEBUG("%s timeout! \n", __FUNCTION__);
}else{
if(saveErrno != EINTR){
// 不等于外部的中断,是由其他错误类型引起的
errno = saveErrno; // 把全局变量errno重新置为当前错误saveErrno
LOG_ERROR("EPollPoller::poll error!");
}
}
return now;
}
fillActiveChannels填写活跃的连接
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_revents(events_[i].events);
activeChannels->push_back(channel); // EventLoop这样就拿到了它的Poller返回的所有发生事件的Channel列表了
}
}
六、EPollPoller.cc
#include "EPollPoller.h"
#include "Channel.h"
#include "Logger.h"
#include "Timestamp.h"
#include <errno.h>
#include <unistd.h>
#include <string.h>
// 标识channel的状态
const int kNew = -1; // Channel从来没有添加到epoll或者从epoll和Poller中的map都删除 Channel的成员index_初始化-1
const int kAdded = 1; // Channel已经添加到了epoll和Poller中的map
const int kDeleted = 2; // Channel从epoll红黑树中删除了,但是还存在于Poller的map中
// 构造函数,调用了epoll_create
EPollPoller::EPollPoller(EventLoop* loop)
: Poller(loop)
, epollfd_(epoll_create1(EPOLL_CLOEXEC))
, events_(kInitEventListSize) // vector<epoll_event>
{
if(epollfd_ < 0){
LOG_FATAL("epoll_create error:%d \n", errno);
}
}
EPollPoller::~EPollPoller(){
// epoll结束的时候,关闭内核事件表fd
::close(epollfd_);
}
// EventLoop创建ChannelList,并把ChannelList传到EPollPoller::poll,poll会把发生事件的channel(即活跃的)通过activeChannels填到EventLoop的成员变量ChannelList中
Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels){
// 实际上应该用LOG_DEBUG更合适
LOG_INFO("func=%s => fd total count : %lu \n", __FUNCTION__, channels_.size());
// events_.begin()返回首元素的迭代器,也就是首元素的地址,是面向对象的,解引用后就是首元素的值,然后取地址就得到了vector封装的底层数组的首地址
// epoll_wait返回后,events_底层的数组的前numEvents元素就是所有发生事件的epoll_event
int numEvents = ::epoll_wait(epollfd_, &(*events_.begin()), static_cast<int>(events_.size()), timeoutMs);
// 全局变量errno,poll可能在多个线程中的eventloop被调用,被读写,所以先用局部变量存起来
int saveErrno = errno;
Timestamp now(Timestamp::now());
if(numEvents > 0){
LOG_INFO("%d events happened \n", numEvents);
fillActiveChannels(numEvents, activeChannels); // 这里每次传入的都是一个空vector,把发生事件的Channel添加到EventLoop的成员变量ChannelList activeChannels中
if(numEvents == (int)events_.size()){
// 发生事件的fd和成员变量EventList的长度相同,需要扩容
events_.resize(events_.size() * 2);
}
}else if(numEvents == 0){
// 没有事件发生,超时
LOG_DEBUG("%s timeout! \n", __FUNCTION__);
}else{
if(saveErrno != EINTR){
// 不等于外部的中断,是由其他错误类型引起的
errno = saveErrno; // 把全局变量errno重新置为当前错误saveErrno
LOG_ERROR("EPollPoller::poll error!");
}
}
return now;
}
// Channel调用update remove ==> EventLoop updateChannel removeChannel ==> Poller updateChannel removeChannel
// epoll_ctl EPOLL_CTL_ADD EPOLL_CTL_MOD
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){
if(index == kNew){
// 如果从未添加到EPollPoller中,那么将fd和对应channel对象注册到Poller中的成员map中
int fd = channel->fd();
channels_[fd] = channel;
}
channel->set_index(kAdded);
update(EPOLL_CTL_ADD, channel); // epoll_ctl EPOLL_CTL_ADD
}else{
// index == kAdded
int fd = channel->fd();
if(channel->isNoneEvent()){
// 对任何事件都不感兴趣,从epoll红黑树中删除
channel->set_index(kDeleted);
update(EPOLL_CTL_DEL, channel);
}else{
// 对某些事件感兴趣
update(EPOLL_CTL_MOD, channel);
}
}
}
// eventloop中删除channel(fd) epoll_ctl EPOLL_CTL_DEL
void EPollPoller::removeChannel(Channel* channel){
int fd = channel->fd();
channels_.erase(fd); // 先在Poller里的map中删除
LOG_INFO("func=%s => fd=%d \n", __FUNCTION__, fd);
int index = channel->index();
if(index == kAdded){
update(EPOLL_CTL_DEL, channel); // 从epoll红黑树中删除
}
channel->set_index(kNew); // 最终的结果就是要使得Channel的状态是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_revents(events_[i].events);
activeChannels->push_back(channel); // EventLoop就拿到了它的Poller返回的所有发生事件的Channel列表了
}
}
// epoll_ctl EPOLL_CTL_ADD,EPOLL_CTL_DEL,EPOLL_CTL_MOD 更新channel
void EPollPoller::update(int operation, Channel* channel){
epoll_event event;
memset(&event, 0, sizeof(event));
int fd = channel->fd();
event.events = channel->events();
event.data.fd = fd;
event.data.ptr = channel;
if(::epoll_ctl(epollfd_, operation, fd, &event) < 0){
// epoll_ctl返回值小于0,则出错
if(operation == EPOLL_CTL_DEL){
LOG_ERROR("epoll_ctl delete error:%d\n", errno);
}else{
LOG_FATAL("epoll_ctl add / modify error:%d\n", errno);
}
}
}