muduo源码学习(15)-IO复用的封装
来源:互联网 发布:美团大数据 在哪看 编辑:程序博客网 时间:2024/05/18 00:37
上篇从大体上介绍了muduo网络库部分的类和封装的思路,现在具体分析其中的类。首先从简单的IO复用的封装开始。
在分析IO复用之前首先要分析Channel,在net/Channel.h文件中
/// A selectable I/O channel.////// This class doesn't own the file descriptor./// The file descriptor could be a socket,/// an eventfd, a timerfd, or a signalfdclass Channel : boost::noncopyable{ public: typedef boost::function<void()> EventCallback; typedef boost::function<void(Timestamp)> ReadEventCallback; Channel(EventLoop* loop, int fd); ~Channel();//处理事件 void handleEvent(Timestamp receiveTime); void setReadCallback(const ReadEventCallback& cb) { readCallback_ = cb; } void setWriteCallback(const EventCallback& cb) { writeCallback_ = cb; } void setCloseCallback(const EventCallback& cb) { closeCallback_ = cb; } void setErrorCallback(const EventCallback& cb) { errorCallback_ = cb; } /// Tie this channel to the owner object managed by shared_ptr, /// prevent the owner object being destroyed in handleEvent. void tie(const boost::shared_ptr<void>&); int fd() const { return fd_; } int events() const { return events_; } void set_revents(int revt) { revents_ = revt; } // used by pollers int revents() const { return revents_; } bool isNoneEvent() const { return events_ == kNoneEvent; } void enableReading() { events_ |= kReadEvent; update(); } void disableReading() { events_ &= ~kReadEvent; update(); } void enableWriting() { events_ |= kWriteEvent; update(); } void disableWriting() { events_ &= ~kWriteEvent; update(); } void disableAll() { events_ = kNoneEvent; update(); } bool isWriting() const { return events_ & kWriteEvent; } // for Poller int index() { return index_; } void set_index(int idx) { index_ = idx; } // for debug string reventsToString() const; void doNotLogHup() { logHup_ = false; } EventLoop* ownerLoop() { return loop_; } void remove(); private://更新在poll中 void update();//处理事件 void handleEventWithGuard(Timestamp receiveTime); //代表不同事件的整数 static const int kNoneEvent; static const int kReadEvent; static const int kWriteEvent; //所属的EventLoop EventLoop* loop_;//描述符 const int fd_;//监听的事件 int events_; //活跃的事件 int revents_;//在poll类中的下标,初始化为-1 int index_; // used by Poller. bool logHup_; //保存弱引用 boost::weak_ptr<void> tie_;//是否获得弱引用 bool tied_;//是否处于处理事件的函数中 bool eventHandling_;//事件的回调函数 ReadEventCallback readCallback_; EventCallback writeCallback_; EventCallback closeCallback_; EventCallback errorCallback_;};
主要属性就是文件描述符,处理所关心事件的函数指针,所监听的事件,活跃的事件,所属的EventLoop,以及一些属性的设置获取方法。
net/Channel.cc
const int Channel::kNoneEvent = 0;const int Channel::kReadEvent = POLLIN | POLLPRI;const int Channel::kWriteEvent = POLLOUT;Channel::Channel(EventLoop* loop, int fd__) : loop_(loop), fd_(fd__), events_(0), revents_(0), index_(-1), logHup_(true), tied_(false), eventHandling_(false){}Channel::~Channel(){ assert(!eventHandling_);}//获得所属TcpConnection的弱引用void Channel::tie(const boost::shared_ptr<void>& obj){ tie_ = obj; tied_ = true;}//修改在poller中的该channal,如果poller中的有关集合中还为由此描述符,则添加到其中//最终调用了所属loop中的poll对象的update函数void Channel::update(){ loop_->updateChannel(this);}//删除在poller集合中于此相同的channal,即不在被poll监听void Channel::remove(){ assert(isNoneEvent()); loop_->removeChannel(this);}//处理事件,在pool循环中,活跃的描述符被遍历调用该各自的回调函数void Channel::handleEvent(Timestamp receiveTime){ boost::shared_ptr<void> guard; if (tied_) { //将弱引用提升,TcpConnection的对象的引用计数+1,变为2 guard = tie_.lock();if (guard) { handleEventWithGuard(receiveTime); }//引用计数变为2 } else { handleEventWithGuard(receiveTime); } //guard销毁,TcpConnection引用计数减一,变为1}//根据revents_调用不同的回调函数void Channel::handleEventWithGuard(Timestamp receiveTime){//表示正在调用该描述符的事件处理的回调函数//在析构若该函数还在执行,则出错 eventHandling_ = true;//注意//服务端主动关闭,客户端read()返回0,关闭连接,服务端会同时接受到POLLHUP和POLLIN//而如果是客户端主动断开连接,则服务端只会接收到POLLIN if ((revents_ & POLLHUP) && !(revents_ & POLLIN)) { if (logHup_) { LOG_WARN << "Channel::handle_event() POLLHUP"; } if (closeCallback_) closeCallback_(); } if (revents_ & POLLNVAL) { LOG_WARN << "Channel::handle_event() POLLNVAL"; } if (revents_ & (POLLERR | POLLNVAL)) { if (errorCallback_) errorCallback_(); } if (revents_ & (POLLIN | POLLPRI | POLLRDHUP)) { if (readCallback_) readCallback_(receiveTime); } if (revents_ & POLLOUT) { if (writeCallback_) writeCallback_(); } eventHandling_ = false;}//打印,在loop中可输出日志string Channel::reventsToString() const{ std::ostringstream oss; oss << fd_ << ": "; if (revents_ & POLLIN) oss << "IN "; if (revents_ & POLLPRI) oss << "PRI "; if (revents_ & POLLOUT) oss << "OUT "; if (revents_ & POLLHUP) oss << "HUP "; if (revents_ & POLLRDHUP) oss << "RDHUP "; if (revents_ & POLLERR) oss << "ERR "; if (revents_ & POLLNVAL) oss << "NVAL "; return oss.str().c_str();
其中主要的就是handleEvent函数,该函数调用了handleEventWithGuard函数,会根据活跃事件的类型调用不同的函数。在EventLoop的循环loop()中,就是在poll返回活跃的Channel后,遍历调用其handleEvent函数。
接下来看真正的IO复用的封装。首先是一个抽象类Poller
class Channel;////// Base class for IO Multiplexing////// This class doesn't own the Channel objects.class Poller : boost::noncopyable{ public: typedef std::vector<Channel*> ChannelList; Poller(EventLoop* loop); virtual ~Poller();// /// Polls the I/O events. /// Must be called in the loop thread. //调用epoll/poll,将活跃的事件存放到activeChannels中 virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0; /// Changes the interested I/O events. /// Must be called in the loop thread. //¸更新channel virtual void updateChannel(Channel* channel) = 0; /// Remove the channel, when it destructs. /// Must be called in the loop thread. //移除channel virtual void removeChannel(Channel* channel) = 0; static Poller* newDefaultPoller(EventLoop* loop); void assertInLoopThread() { ownerLoop_->assertInLoopThread(); } private: //所属的Eventloop EventLoop* ownerLoop_;};
该类定义了几个虚函数,poll函数的具体实现类 net/Poller/PollPoller.h
class PollPoller : public Poller{ public: PollPoller(EventLoop* loop); virtual ~PollPoller(); virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels); virtual void updateChannel(Channel* channel); virtual void removeChannel(Channel* channel); private: void fillActiveChannels(int numEvents, ChannelList* activeChannels) const; typedef std::vector<struct pollfd> PollFdList; typedef std::map<int, Channel*> ChannelMap; PollFdList pollfds_; //存放channel *的map ChannelMap channels_;};主要是存储容器,在PollPoller类具体实现中,首先看一下poll的具体实现
Timestamp PollPoller::poll(int timeoutMs, ChannelList* activeChannels){ // XXX pollfds_ shouldn't change //pollfd_ ÀàÐÍΪvector<struct pollfd>, //调用poll()函数 int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs); Timestamp now(Timestamp::now()); if (numEvents > 0) { LOG_TRACE << numEvents << " events happended";//将pollfds中活跃的描述符对于的Channel,添加到activeChannels中 fillActiveChannels(numEvents, activeChannels); } else if (numEvents == 0) { LOG_TRACE << " nothing happended"; } else { LOG_SYSERR << "PollPoller::poll()"; } return now;}调用poll,之后调用fillActiveChannels,fillActiveChannels实现如下:
//将活跃的描述符对于的channel添加到activeChannels中void PollPoller::fillActiveChannels(int numEvents, ChannelList* activeChannels) const{//遍历 for (PollFdList::const_iterator pfd = pollfds_.begin(); pfd != pollfds_.end() && numEvents > 0; ++pfd) { //如果是活跃的 if (pfd->revents > 0) { --numEvents; ChannelMap::const_iterator ch = channels_.find(pfd->fd);//找到对应的channel assert(ch != channels_.end()); Channel* channel = ch->second; assert(channel->fd() == pfd->fd); //设置活跃的事件 channel->set_revents(pfd->revents); // pfd->revents = 0; activeChannels->push_back(channel);//添加到activeChannels } }}
removeChannel的实现如下
//移除void PollPoller::removeChannel(Channel* channel){ Poller::assertInLoopThread(); LOG_TRACE << "fd = " << channel->fd(); assert(channels_.find(channel->fd()) != channels_.end()); assert(channels_[channel->fd()] == channel); assert(channel->isNoneEvent());//获得对应的pollfds_中的下标 int idx = channel->index(); assert(0 <= idx && idx < static_cast<int>(pollfds_.size())); const struct pollfd& pfd = pollfds_[idx]; (void)pfd; assert(pfd.fd == -channel->fd()-1 && pfd.events == channel->events()); //移除map size_t n = channels_.erase(channel->fd()); assert(n == 1); (void)n; //如果是在pollfds_的最后一个元素,直接移除 if (implicit_cast<size_t>(idx) == pollfds_.size()-1) { pollfds_.pop_back(); } //如果移除的元素不在pollfds_即vector的末尾,则先与末尾元素交换顺序,在移除末尾元素 else { //获得末尾的元素的fd int channelAtEnd = pollfds_.back().fd;//交换 iter_swap(pollfds_.begin()+idx, pollfds_.end()-1); if (channelAtEnd < 0) { channelAtEnd = -channelAtEnd-1; }//设置index channels_[channelAtEnd]->set_index(idx); pollfds_.pop_back(); }移除channel时,需要从map和vector中移除。从map中移除比较容易,而从vector中移除是,为了提高效率,首先channel中的index记录了描述符对应的struct pollfd在pollfds_中的位置,以便于快速找到要移除的pollfd,之后,如果要移除的元素在pollfds_末尾,则直接移除,否则,先与末尾元素交换位置后再移除末尾元素,这与vector底层是数组的有关。
updateChannel实现:
//¸更新map和vectorvoid PollPoller::updateChannel(Channel* channel){ Poller::assertInLoopThread(); LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();//index<0,说明该描述符还未添加到pollfds_ 中,执行添加操作 if (channel->index() < 0) { // a new one, add to pollfds_ assert(channels_.find(channel->fd()) == channels_.end());//添加 struct pollfd pfd; pfd.fd = channel->fd(); pfd.events = static_cast<short>(channel->events()); pfd.revents = 0;//添加 pollfds_.push_back(pfd);//保存位置 int idx = static_cast<int>(pollfds_.size())-1;channel->set_index(idx);//保存到map channels_[pfd.fd] = channel; }//以经存在,修改监听事件 else { // update existing one assert(channels_.find(channel->fd()) != channels_.end()); assert(channels_[channel->fd()] == channel); int idx = channel->index(); assert(0 <= idx && idx < static_cast<int>(pollfds_.size())); struct pollfd& pfd = pollfds_[idx]; assert(pfd.fd == channel->fd() || pfd.fd == -channel->fd()-1); pfd.events = static_cast<short>(channel->events()); //修改监听事件 pfd.revents = 0;//不再监听事件 if (channel->isNoneEvent()) { // ignore this pollfd pfd.fd = -channel->fd()-1; } }}
该函数狐区分是添加描述符还是修改描述非的监听事件。
epoll的实现类如下:
class EPollPoller : public Poller{ public: EPollPoller(EventLoop* loop); virtual ~EPollPoller(); virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels); virtual void updateChannel(Channel* channel); virtual void removeChannel(Channel* channel); private: // static const int kInitEventListSize = 16; void fillActiveChannels(int numEvents, ChannelList* activeChannels) const; void update(int operation, Channel* channel); typedef std::vector<struct epoll_event> EventList; typedef std::map<int, Channel*> ChannelMap; //epoll_ctl的返回值 int epollfd_; //调用epoll_wait得到的活跃描述符数组 EventList events_; //存放fd,channel的map ChannelMap channels_;};也是有存放channel的map,poll函数实现如下:
Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels){//调用epoll_wait int numEvents = ::epoll_wait(epollfd_, &*events_.begin(), static_cast<int>(events_.size()), timeoutMs); Timestamp now(Timestamp::now()); if (numEvents > 0) { LOG_TRACE << numEvents << " events happended";//将描述符对应的 channel添加到activeChannels中 fillActiveChannels(numEvents, activeChannels);//扩大容量 if (implicit_cast<size_t>(numEvents) == events_.size()) { events_.resize(events_.size()*2); } } else if (numEvents == 0) { LOG_TRACE << " nothing happended"; } else { LOG_SYSERR << "EPollPoller::poll()"; } // return now;}fillActiveChannels如下
void EPollPoller::fillActiveChannels(int numEvents, ChannelList* activeChannels) const{ assert(implicit_cast<size_t>(numEvents) <= events_.size()); for (int i = 0; i < numEvents; ++i) //遍历 { Channel* channel = static_cast<Channel*>(events_[i].data.ptr);#ifndef NDEBUG int fd = channel->fd(); ChannelMap::const_iterator it = channels_.find(fd); assert(it != channels_.end()); assert(it->second == channel);#endif channel->set_revents(events_[i].events); activeChannels->push_back(channel); //添加 }}
void EPollPoller::removeChannel(Channel* channel){ Poller::assertInLoopThread(); //»ñÈ¡¸Ãchannal¶ÔÓ¦µÄÃèÊö·û 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);}因为内核管理的文件描述符和struct event,因此执行从map中移除对应的channel,updateChannel实现如下:
void EPollPoller::updateChannel(Channel* channel){ Poller::assertInLoopThread(); LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events(); //index用于区分是否是新的描述符 const int index = channel->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; } else // index == kDeleted { assert(channels_.find(fd) != channels_.end()); assert(channels_[fd] == channel); } channel->set_index(kAdded); //设置状态 update(EPOLL_CTL_ADD, channel); //添加到epoll中 } 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()) { update(EPOLL_CTL_DEL, channel); //调用updata channel->set_index(kDeleted); //移除 } else { update(EPOLL_CTL_MOD, channel); } }}
uodata函数如下:
void EPollPoller::update(int operation, Channel* channel){ struct epoll_event event; bzero(&event, sizeof event); event.events = channel->events(); event.data.ptr = channel; int fd = channel->fd(); if (::epoll_ctl(epollfd_, operation, fd, &event) < 0) { if (operation == EPOLL_CTL_DEL) { LOG_SYSERR << "epoll_ctl op=" << operation << " fd=" << fd; } else { LOG_SYSFATAL << "epoll_ctl op=" << operation << " fd=" << fd; } }}根据operation调用epoll_ctl函数.
在默认情况下,有
Poller* Poller::newDefaultPoller(EventLoop* loop){ if (::getenv("MUDUO_USE_POLL")) { return new PollPoller(loop); } else { return new EPollPoller(loop); }}该函数自动选择使用epoll的类。
总的来说,用户执行调用poll函数,该函数将活跃的描述符对应的Channel存放在vector容器中返回,然后循环遍历每一个 channel,调用其handleEvent即可。
阅读全文
0 0
- muduo源码学习(15)-IO复用的封装
- muduo网络库学习(一)对io复用的封装Poller,面向对象与基于对象
- muduo源码学习(5)-线程封装
- muduo源码学习(19)-socket封装
- muduo源码学习(20)-Acceptor封装
- muduo源码学习(11)-日志类封装1
- muduo源码学习(12)-日志类封装2
- muduo源码分析之使用封装的Buffer读取数据
- muduo源码学习(1)
- libevent源码学习----io多路复用的封装和使用
- muduo源码分析:异常类封装
- muduo源码分析:线程类Thread封装
- muduo源码分析:reactor模型封装
- muduo日志类的封装
- muduo库的Socket封装
- muduo源码学习(7)-队列
- muduo源码学习(8)-ThreadPool
- muduo源码学习(9)-单例类
- 嵌入式系统架构
- poj 3904 Sky Code(容斥原理)
- sqlserver存储过程中动态添加链接服务器示例(存储过程操作其他数据库服务器)
- @responseBody注解的使用
- 自定义控件---仿PhotoView实现图片查看
- muduo源码学习(15)-IO复用的封装
- python中文件上传云存储解析
- AsyncTask+xlist
- 自定义View---继承ViewGroup动效
- HDU 6143 Killer Names(容斥定理)
- HashMap的扩容机制, ConcurrentHashMap和Hashtable主要区别
- 请填写正确的微信账号信息
- Eclipse,执行project的clean操作,无效,即没有重新编译生成class文件
- UITextField 日常使用一些特别的地方