Poller是EpollPoller和poll的基类实现,是一个虚基类,主要封装了IO多路复用的核心接口
// muduo库中多路事件分发器的核心IO复用模块
class Poller
{
public:
using ChannelList = std::vector<Channel *>;
Poller(EventLoop *loop);
virtual ~Poller() = default;
// 给所有IO复用保留统一的接口
virtual Timestamp poll(int timeoutMs, ChannelList *activeChannels) = 0; //EventLoop对象通过这个函数拿到具体发生关心事件的通道有哪些
virtual void updateChannel(Channel *channel) = 0; //修改通道关心的事件
virtual void removeChannel(Channel *channel) = 0; //移除通道关心的事件
protected:
// map的key:sockfd value:sockfd所属的channel通道类型
using ChannelMap = std::unordered_map<int, Channel *>;
ChannelMap channels_;
private:
EventLoop *ownerLoop_; // 定义Poller所属的事件循环EventLoop
};
poller类可以判断是否监听了某个通道(实际上看EventLoop对象是否绑定了该通道),也即hasChannel函数
// 判断参数channel是否在当前的Poller当中
bool hasChannel(Channel *channel) const;
bool Poller::hasChannel(Channel *channel) const
{
auto it = channels_.find(channel->fd());
return it != channels_.end() && it->second == channel;
}
// EventLoop可以通过该接口获取默认的IO复用的具体实现, 得到epoll实例亦或是poll实例
static Poller *newDefaultPoller(EventLoop *loop);
继承至Poller类,具体实现epoll多路复用的子类
/**
* epoll的使用:
* 1. epoll_create
* 2. epoll_ctl (add, mod, del)
* 3. epoll_wait
**/
class Channel;
class EPollPoller : public Poller
{
public:
EPollPoller(EventLoop *loop);
~EPollPoller() override;
// 重写基类Poller的抽象方法
Timestamp poll(int timeoutMs, ChannelList *activeChannels) override; //epoll模式下,EventLoop对象通过这个函数拿到具体发生关心事件的通道有哪些
void updateChannel(Channel *channel) override;
void removeChannel(Channel *channel) override;
private:
static const int kInitEventListSize = 16; //初始化事件个数
// 填写活跃的连接
void fillActiveChannels(int numEvents, ChannelList *activeChannels) const;
// 更新channel通道 其实就是调用epoll_ctl
void update(int operation, Channel *channel);
using EventList = std::vector<epoll_event>; // C++中可以省略struct 直接写epoll_event即可
int epollfd_; // epoll_create创建返回的fd保存在epollfd_中
EventList events_; // 用于存放epoll_wait返回的所有发生的事件的文件描述符事件集
};
EpollPoller构造:首先调用基类Poller的构造绑定EventLoop对象,再通过epoll_create1方法构造epollfd, 并初始化相应监听事件的数量
EPollPoller::EPollPoller(EventLoop *loop)
: Poller(loop)
, epollfd_(::epoll_create1(EPOLL_CLOEXEC))
, events_(kInitEventListSize) // vector(16)
{
if (epollfd_ < 0)
{
LOG_FATAL("epoll_create error:%d \n", errno);
}
}
epoll的io多路复用实现,即重写基类Poller的poll接口
Timestamp EPollPoller::poll(int timeoutMs, ChannelList *activeChannels)
{
// 由于频繁调用poll 实际上应该用LOG_DEBUG输出日志更为合理 当遇到并发场景 关闭DEBUG日志提升效率
LOG_INFO("func=%s => fd total count:%lu\n", __FUNCTION__, channels_.size());
//调用epoll_wait方法监听事件,返回发生活跃事件fd的个数
int numEvents = ::epoll_wait(epollfd_, &*events_.begin(), static_cast<int>(events_.size()), timeoutMs);
int saveErrno = errno;
Timestamp now(Timestamp::now());
if (numEvents > 0)
{
LOG_INFO("%d events happend\n", numEvents); // LOG_DEBUG最合理
fillActiveChannels(numEvents, activeChannels); //填充活跃fd对应的通道,并放入活跃通道列表中
if (numEvents == events_.size()) // 扩容操作
{
events_.resize(events_.size() * 2); //每次2倍扩容
}
}
else if (numEvents == 0)
{
LOG_DEBUG("%s timeout!\n", __FUNCTION__);
}
else
{
if (saveErrno != EINTR)
{
errno = saveErrno;
LOG_ERROR("EPollPoller::poll() error!");
}
}
return now;
}
// 填写活跃的连接
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列表了
}
}
与之绑定的EventLoop对象调用update()和remove()方法实际上上是调用EpollPoller的updateChannel()和removeChannel()方法。注意每添加一个新的fd,channels都会将fd与通道记录在map中,每删除一个fd,都会将这个fd对应的map映射删除。
const int kNew = -1; // 某个channel还没添加至Poller // channel的成员index_初始化为-1
const int kAdded = 1; // 某个channel已经添加至Poller
const int kDeleted = 2; // 某个channel已经从Poller删除
// 调用流程:channel update remove => EventLoop updateChannel removeChannel => Poller updateChannel removeChannel
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)
{
int fd = channel->fd();
channels_[fd] = channel; //将新的fd设置进map
}
else // index == kAdd
{
}
channel->set_index(kAdded);
update(EPOLL_CTL_ADD, channel);
}
else // channel已经在Poller中注册过了
{
int fd = channel->fd();
if (channel->isNoneEvent())
{
update(EPOLL_CTL_DEL, channel);
channel->set_index(kDeleted);
}
else
{
update(EPOLL_CTL_MOD, channel);
}
}
}
// 从Poller中删除channel
void EPollPoller::removeChannel(Channel *channel)
{
int fd = channel->fd();
channels_.erase(fd); //删除之后channel_map需要移除对应的fd
LOG_INFO("func=%s => fd=%d\n", __FUNCTION__, fd);
int index = channel->index();
if (index == kAdded)
{
update(EPOLL_CTL_DEL, channel);
}
channel->set_index(kNew);
}
而实际上的调用还封装了一层:Poller::updateChannel/removeChannel -> Poller::update()
Poller::update()才是真正的修改通道所绑定的fd的事件
// 更新channel通道 其实就是调用epoll_ctl add/mod/del
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; //绑定fd对应的Channel
if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)
{
if (operation == EPOLL_CTL_DEL)
{
LOG_ERROR("epoll_ctl del error:%d\n", errno);
}
else
{
LOG_FATAL("epoll_ctl add/mod error:%d\n", errno);
}
}
}
EPollPoller释放操作,关系epollfd即可
EPollPoller::~EPollPoller()
{
::close(epollfd_);
}