前言
本篇文章所讲的是本人的个人项目仿muduo库高并发服务器中的Poller模块实现部分。
Poller模块介绍:
这也是一个比较简单的基础功能模块,主要提供对epoll系统调封装的接口。
- 功能:对任意描述符进行IO事件监控
- 意义:封装epoll,简化描述符事件监控操作
- 功能接口:
- 添加事件监控(针对Channel模块 )
- 修改事件监控
- 移除事件监控
Channel模块启用监控、移除监控等功能,都是通过调用该模块的封装完成的。
模块设计
- 需具备
epoll的操作句柄。 - 拥有
struct epoll_event结构数组,用于监控时保存所有活跃事件。 - 利用哈希表管理描述符及其对应的事件管理
Channel对象。
逻辑流程
- 对描述符进行监控,通过
Channel明确描述符需要监控的事件。 - 当描述符就绪时(描述符有事件就绪时),借助哈希表根据描述符找到对应的
Channel(通过Channel确定事件的处理方式),并返回就绪描述符对应的Channel。
c++
const int MAX_EPOLLEVENTS = 1024;
class Poller
{
private:
int _epfd; //epoll文件描述符
struct epoll_event _evs[MAX_EPOLLEVENTS]; //存放就绪事件的数组
std::unordered_map<int,Channel*> _channels; //建立epfd到channel指针的映射
//对epoll的直接操作
void Update(Channel* channel,int op)
{
// int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev);
int fd = channel->Fd();
struct epoll_event ev;
ev.data.fd = fd;
ev.events = channel->Events();
int ret = epoll_ctl(_epfd,op,fd,&ev);
if(ret < 0)
{
ERR_LOG("EPOLLCTL FAILED!!");
}
}
//判断一个Channel是否已经添加了事件监控
bool HasChannel(Channel* channel)
{
auto it = _channels.find(channel->Fd());
if(it == _channels.end())
{
return false;
}
return true;
}
public:
Poller()
{
_epfd = epoll_create(MAX_EPOLLEVENTS);
if(_epfd < 0)
{
ERR_LOG("EPOLL CREATE FAILED!!");
abort();//直接退出
}
}
//添加或者修改监控事件
void UpdateEvent(Channel* channel)
{
bool ret = HasChannel(channel);
if(ret == false)
{
//不存在则添加
_channels.insert(std::make_pair(channel->Fd(),channel));
Update(channel,EPOLL_CTL_ADD);
}
//存在则修改
Update(channel,EPOLL_CTL_MOD);
}
//移除监控
void RemoveEvent(Channel* channel)
{
auto it = _channels.find(channel->Fd());
if(it != _channels.end())
{
_channels.erase(it);
}
Update(channel,EPOLL_CTL_DEL);
}
//开始监控,返回活跃连接
void Poll(std::vector<Channel*>* active)
{
// int epoll_wait(int epfd, struct epoll_event *evs, int maxevents, int timeout)
int nfds = epoll_wait(_epfd,_evs,MAX_EPOLLEVENTS,-1);//阻塞模式
if(nfds < 0)
{
if(errno == EINTR)
{
return;
}
ERR_LOG("EPOLL WAIT ERROR:%s",strerror(errno));
abort();
}
for(int i = 0;i < nfds;i++)
{
auto it = _channels.find(_evs[i].data.fd);
assert(it != _channels.end());
it->second->SetEvents(_evs[i].events);//设置实际就绪事件
active->push_back(it->second);
}
}
};