封装底层 I/O 多路复用机制(epoll
)的 抽象类 Poller
Poller
是一个抽象基类(有纯虚函数)。
功能 |
说明 |
IO复用抽象层 |
提供统一接口,支持 epoll、poll 等底层实现 |
Channel 管理 |
注册、更新、删除所有监听的 Channel |
活跃事件收集 |
收集并返回所有有事件的 Channel 供 EventLoop 分发 |
与 EventLoop 绑定 |
每个 Poller 服务于一个 EventLoop |
class Poller: noncopyable { public: using ChannelList=std::vector<Channel*>;//定义一个别名 ChannelList,表示当前活跃的 Channel* 列表,用于事件分发 Poller(EventLoop*loop);//每个 Poller 实例都只为一个 EventLoop 服务 virtual ~Poller()=default;//虚析构函数,支持多态析构,释放派生类资源(比如 EPollPoller)。 //给所有IO复用保留统一接口 //核心接口,等待 IO 事件的发生(如调用 epoll_wait()),超时时间为 timeoutMs 毫秒。 当有事件发生时,将活跃的 Channel* 填入 activeChannels 中。 返回值是当前时间戳(Timestamp 一般是 typedef 的封装类型)。 virtual Timestamp poll(int timeoutMS,ChannelList*activeChannels )=0; //向 Poller 注册或更新一个 Channel 的事件(如监听可读、可写事件)。 virtual void updateChannel(Channel *channel)=0; //将某个 Channel 从 Poller 中移除。 virtual void removeChannel(Channel *channel)=0; //判断一个poller是否有channel bool hasChannel(Channel *channel)const; //拿到默认的一个io复用处理 //工厂方法,根据系统默认配置创建一个合适的 Poller 实例(如使用 epoll) static Poller*newDefaultPoller(EventLoop *Loop); protected: //key: sockfd value:sockfd所属的channel通道类型 //key 是 fd,value 是对应的 Channel*。 //管理所有注册到 Poller 的 Channel,方便更新、查询、删除。 using ChannelMap=std::unordered_map<int,Channel*>; ChannelMap channels_; private: EventLoop* ownerLoop_;//定义poller所属的事件循环EventLoop; };
//作用是初始化 Poller,把它绑定到所属的 EventLoop 对象上,方便 Poller 在需要时访问 EventLoop 的信息。 Poller::Poller(EventLoop *loop):ownerLoop_(loop) { } // 判断 Poller 是否管理某个 Channel //返回 true 表示这个 Poller 正在管理(监听)这个 Channel 对应的 fd bool Poller::hasChannel(Channel *channel)const { auto it=channels_.find(channel->fd()); return it!=channels_.end()&&it->second==channel; } //静态工厂方法 //对调用者来说,只需调用 Poller::newDefaultPoller(loop),得到一个指向 Poller 抽象基类的指针,不用关心是 PollPoller 还是 EPollPoller。 //返回值是 Poller*,即抽象基类指针,但实际指向的是具体子类实例,满足多态调用。 Poller*Poller::newDefaultPoller(EventLoop *Loop) { if(::getenv("MUDUO_USE_POLL")) { return nullptr;//生成poll的实例 } else { return new EPollPoller(Loop);//生成epoll的实例 } }
子类(EpollPoller)
功能 |
说明 |
管理 Channel 与 fd 的映射 |
|
负责 epoll 的创建与销毁 |
构造函数中创建 ,析构时关闭 |
实现三大核心接口: |
- :调用 ,返回活跃事件的 Channel 列表 :通过 添加/修改监听的 fd :从 epoll 中删除 fd |
提供 |
将 返回的事件,转换为活跃的 列表,供 使用 |
使用 epoll 高效监听海量连接 |
相比 , 支持边缘触发、O(1) 事件分发,不受 FD 上限影响 |
成员变量
//在调用 epoll_wait 时需要一个事件列表缓冲区,这里用 events_ 来存储活跃的 epoll_event,初始空间预留16个事件。 static const int KInitEventListSize=16; //更新channel通道 using EventList=std::vector<epoll_event>;//用于存储 epoll_wait 返回的活跃事件数组。 int epollfd_;存储 epoll 实例的文件描述符。 EventList events_;//保存活跃事件的数组。 const int KNew=-1;//channel未添加到poller channel成员变量index -1 const int KAdded=1;//channel已添加到poller const int KDeleted=2;//channel从poller删除
成员方法实现
构造函数 EPollPoller::EPollPoller(EventLoop *loop): Poller(loop), // 调用基类 Poller 的构造函数,保存所属 EventLoop epollfd_(::epoll_create1(EPOLL_CLOEXEC))//当调用 exec 系列函数时自动关闭该 fd。 ,events_(KInitEventListSize) // 初始化事件数组大小为 16(默认值) { if(epollfd_<0)//// 创建失败则日志致命错误,程序终止 { LOG_FATAL("epoll_create error:%d\n",errno); } } 析构函数 EPollPoller::~EPollPoller() { ::close(epollfd_);//当 EPollPoller 对象销毁时,关闭内部使用的 epoll 文件描述符(epollfd_),释放内核资源。 } //该函数是 epoll 的 wait() 封装 —— 等待事件发生,并将活跃的 Channel 填充到 activeChannels 中返回。 Timestamp EPollPoller::poll(int timeoutMS,ChannelList *activeChannels) { //打印日志:当前 poll 操作中,Poller 管理了多少个 fd(即 Channel 数量) LOG_INFO("func=%s=> fd total count:%lu\n",__FUNCTION__,channels_.size()); //返回值是触发的事件数量 //events_用于保存触发事件的 epoll_event //&*events_.begin(): 拿到底层数组首地址 int numEvents=::epoll_wait(epollfd_,&*events_.begin(),static_cast<int>(events_.size()),timeoutMS); //立即保存 errno,因为接下来会调用其他系统或日志函数,可能会覆盖它。 int saveError=errno; // 获取当前时间戳,代表“这批事件是何时触发的”。 Timestamp now(Timestamp::now()); if(numEvents>0)// 有事件触发 { LOG_INFO("%d events happened \n",numEvents); //触发的 epoll_event 转换为 Channel 指针,写入到 *activeChannels 中,以便 EventLoop 进一步调用回调处理。 fillActiveChannels(numEvents,activeChannels); // 如果本轮触发的事件数量等于 events_ 当前容量,说明缓冲区满了,需要扩容(加倍策略),为下次 epoll_wait 提前分配空间。 if(numEvents==events_.size()) { events_.resize(events_.size()*2); } }// 没有事件触发(超时) else if(numEvents==0) { LOG_DEBUG("%s timeout!\n",__FUNCTION__); } else// epoll_wait 返回负数,说明出错了 { if(saveError!=EINTR) { errno=saveError; LOG_ERROR("EpollPoller::poll() error!"); } } return now; // 正确返回一个 Timestamp 对象 } void EPollPoller::updateChannel(Channel *channel)//将 Channel 添加、修改或删除到 epoll 实例 { //根据 Channel 的当前状态(index)决定是否调用 epoll_ctl 的 ADD / MOD / DEL,并同步更新内部状态。 //获取当前 Channel 的状态索引(可能是 KNew / KAdded / KDeleted),用于判断如何处理该 channel const int index=channel->index(); // 打印日志,查看当前是哪个 channel、fd 是多少、感兴趣的事件是什么、当前状态是啥。 LOG_INFO("fuc=%s=> fd=%d events=%d index=%d \n",__FUNCTION__,channel->fd(),channel->events(),index); // 如果当前 channel 是“新注册”或者“已经从 epoll 删除了但仍存在”,都需要重新通过 EPOLL_CTL_ADD 加入 epoll。 if(index==KNew||index==KDeleted) { if(index==KNew)// 如果是全新的 channel(index == -1),将其注册到内部的 channels_ 哈希表中,便于后续通过 fd 找到对应的 Channel。 { int fd=channel->fd(); channels_[fd]=channel; } channel->setindex(KAdded);//无论是新建还是重新添加,都统一标记为 KAdded update(EPOLL_CTL_ADD,channel);//调用封装的 update 方法,用 epoll_ctl(epollfd_, EPOLL_CTL_ADD, ...) 真正注册到内核 epoll 实例 } else//说明这个 channel 已经在 epoll 中注册过(index == KAdded) { int fd=channel->fd();// 取出该 channel 的 fd,用于日志或后续判断。 if(channel->isNoneEvent())// 如果该 channel 当前没有关注任何事件(即 events == 0) //从 epoll 实例中注销(epoll_ctl DEL),并将 index 状态改为 KDeleted { update(EPOLL_CTL_DEL,channel); channel->setindex(KDeleted); } else// 如果仍然对某些事件感兴趣(如 EPOLLIN/EPOLLOUT),则更新内核中该 fd 的监听事件类型(epoll_ctl MOD) { update(EPOLL_CTL_MOD,channel); } } //:从 Poller(epoll 封装)中移除一个 Channel,包括: 从 channels_ 映射中移除; 从 epoll 实例中注销(如果已注册) void EPollPoller::removeChannel(Channel *channel)//epoll_ctr->del //从poller中删除channel { int fd=channel->fd(); int index=channel->index(); channels_.erase(fd); // 从内部的 ChannelMap(std::unordered_map<int, Channel*> channels_)中删除该 channel。 LOG_INFO("fuc=%s=> fd=%d \n",__FUNCTION__,fd); if(index==KAdded)//如果该 channel 当前确实已经注册到了 epoll 实例 { update(EPOLL_CTL_DEL,channel);//在epoll中注册过,要在epoll中删除 } channel->setindex(KNew);//把 channel 的状态设置为 KNew(即未加入 epoll 的状态) } //根据 epoll_wait() 返回的事件列表,找到对应的 Channel 对象,设置其触发的事件类型,并将其加入到 EventLoop 的活跃列表中(activeChannels)。 void EPollPoller::fillActiveChannels(int numEvents,ChannelList *activeChannels)const { for(int i=0;i<numEvents;++i)//遍历 epoll_wait 返回的每一个活跃事件(epoll_event); { //从 epoll_event.data.ptr 中恢复出原始注册时的 Channel* Channel *channel=static_cast<Channel*>(events_[i].data.ptr); LOG_INFO("epoll_wait result: event[%d] channel=%p events=%d\n", i, channel, events_[i].events); //设置 channel 的 实际发生事件(revents) channel->set_revents(events_[i].events); //将该活跃 channel 加入到 EventLoop 的活跃列表中 activeChannels->push_back(channel);//Eventloop拿到了他的poller返回得所有发生事件得channel列表了 } } //将某个 Channel 对象对应的 fd 及其感兴趣事件通过 epoll_ctl 注册、修改或删除到 epoll 实例中 //operation 是 epoll_ctl 的操作类型: EPOLL_CTL_ADD:添加新的 fd; EPOLL_CTL_MOD:修改已有的 fd; EPOLL_CTL_DEL:删除 fd。 void EPollPoller::update(int operation,Channel*channel) { epoll_event event;//创建一个 epoll_event 结构体,用于告诉 epoll 要监听哪些事件; memset(&event,0,sizeof event);//使用 memset 清零,保证结构体初始状态干净 int fd=channel->fd(); event.events=channel->events();//设置 epoll_event 的事件类型(如 EPOLLIN, EPOLLOUT 等),这些事件来自 channel->enableReading() 等方法设置的 events_ 成员。 event.data.ptr=channel;//将 channel 的地址存入 epoll_event 的 data.ptr 字段; LOG_INFO("epoll_ctl op=%d fd=%d channel=%p\n", operation, fd, channel); //调用系统函数 epoll_ctl 将 fd 与事件注册到 epoll 实例中 if(::epoll_ctl(epollfd_,operation,fd,&event)<0) { if(operation==EPOLL_CTL_DEL)//如果是删除操作出错,打印错误日志 { LOG_ERROR("epoll_ctl del error:%d\n",errno); } else//自动exit { LOG_FATAL("epoll_ctl add/mod error:%d\n",errno); } } }