epoll处理

先看看如何使用的:

int main(int argc, char* argv[])
{
  int numThreads = 0;
  if (argc > 1)
  {
    benchmark = true;
    Logger::setLogLevel(Logger::WARN);
    numThreads = atoi(argv[1]);
  }
  EventLoop loop;
  HttpServer server(&loop, InetAddress(8000), "dummy");
  server.setHttpCallback(onRequest);
  server.setThreadNum(numThreads);
  server.start();
  loop.loop();
}

在EventLoop::EventLoop()中会调用

Poller::newDefaultPoller(this)

该函数来源:Poller.h


#ifndef MUDUO_NET_POLLER_H
#define MUDUO_NET_POLLER_H


#include <vector>
#include <boost/noncopyable.hpp>


#include <muduo/base/Timestamp.h>
#include <muduo/net/EventLoop.h>


namespace muduo
{
namespace net
{


class Channel;


///
/// Base class for IO Multiplexing  Poller  为抽象类,子类必须实现接口(poll, updateChannel, removeChannel)函数
/// 
/// 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.
  virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;


  /// Changes the interested I/O events.
  /// Must be called in the loop thread.
  virtual void updateChannel(Channel* channel) = 0;


  /// Remove the channel, when it destructs.
  /// Must be called in the loop thread.
  virtual void removeChannel(Channel* channel) = 0;


  static Poller* newDefaultPoller(EventLoop* loop);    //在哪实现的呢?


  void assertInLoopThread()
  {
    ownerLoop_->assertInLoopThread();
  }


 private:
  EventLoop* ownerLoop_;
};


}
}
#endif  // MUDUO_NET_POLLER_H


Poller.cpp

#include <muduo/net/Poller.h>


using namespace muduo;
using namespace muduo::net;


Poller::Poller(EventLoop* loop)
  : ownerLoop_(loop)
{
}


Poller::~Poller()
{
}


找到了Poller::newDefaultPoller的实现地方DefaultPoller.cc

#include <muduo/net/Poller.h>
#include <muduo/net/poller/PollPoller.h>
#include <muduo/net/poller/EPollPoller.h>


#include <stdlib.h>


using namespace muduo::net;


Poller* Poller::newDefaultPoller(EventLoop* loop)
{
  if (::getenv("MUDUO_USE_POLL"))
  {
    return new PollPoller(loop);
  }
  else
  {
    return new EPollPoller(loop);
  }
}


无非是新建PollPoller对象或者EPollPoller对象

先来看看PollPoller对象

PollPoller.h

#ifndef MUDUO_NET_POLLER_POLLPOLLER_H
#define MUDUO_NET_POLLER_POLLPOLLER_H


#include <muduo/net/Poller.h>


#include <map>
#include <vector>


struct pollfd;


namespace muduo
{
namespace net
{


///
/// IO Multiplexing with poll(2). 继承于抽象类Poller并实现其接口函数poll,updateChannel, removeChannel
///
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;      //对于poll函数而言,一个struct pollfd其中包含socketfd和事件
  typedef std::map<int, Channel*> ChannelMap; //key=socketfd, value=Channel
  PollFdList pollfds_;
  ChannelMap channels_;
};


}
}
#endif  // MUDUO_NET_POLLER_POLLPOLLER_H


PollPoller.cpp

#include <muduo/net/poller/PollPoller.h>


#include <muduo/base/Logging.h>
#include <muduo/base/Types.h>
#include <muduo/net/Channel.h>


#include <assert.h>
#include <poll.h>


using namespace muduo;
using namespace muduo::net;


PollPoller::PollPoller(EventLoop* loop)
  : Poller(loop)
{
}


PollPoller::~PollPoller()
{
}


/*poll

获得发生事件的描述符个数numEvents ,

pollfds_中获得对应的socketfd,再利用key=socketfd在ChannelMap中找到对应的Channel,将其放入activeChannels

*/
Timestamp PollPoller::poll(int timeoutMs, ChannelList* activeChannels)
{
  // XXX pollfds_ shouldn't change
  int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);
  Timestamp now(Timestamp::now());
  if (numEvents > 0)
  {
    LOG_TRACE << numEvents << " events happended";
    fillActiveChannels(numEvents, activeChannels);
  }
  else if (numEvents == 0)
  {
    LOG_TRACE << " nothing happended";
  }
  else
  {
    LOG_SYSERR << "PollPoller::poll()";
  }
  return now;
}

/*利用key=socketfd在ChannelMap中找到对应的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);
      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);
    }
  }
}

/*updateChannel

channel存在于key=socketfd的map为channels_中,利用channel的index()在pollfds_中获得该socketfd对应的

struct pollfd,并将struct pollfd的event修改为channel的events()

*/

void PollPoller::updateChannel(Channel* channel)
{
  Poller::assertInLoopThread();
  LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();
  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);
    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;
    }
  }
}


/*updateChannel

channel存在于key=socketfd的map为channels_中,利用channel的index()在pollfds_中获得该socketfd对应的

struct pollfd,首先将key=channel->fd()从map=channels_中删除,再将其struct pollfdpollfds_中删除

*/

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());
  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());
  size_t n = channels_.erase(channel->fd());
  assert(n == 1); (void)n;
  if (implicit_cast<size_t>(idx) == pollfds_.size()-1)
  {
    pollfds_.pop_back();
  }
  else
  {
    int channelAtEnd = pollfds_.back().fd;
    iter_swap(pollfds_.begin()+idx, pollfds_.end()-1);
    if (channelAtEnd < 0)
    {
      channelAtEnd = -channelAtEnd-1;
    }
    channels_[channelAtEnd]->set_index(idx);
    pollfds_.pop_back();
  }
}


再来看看EPollPoller类[LT模式]

#ifndef MUDUO_NET_POLLER_EPOLLPOLLER_H
#define MUDUO_NET_POLLER_EPOLLPOLLER_H


#include <muduo/net/Poller.h>


#include <map>
#include <vector>


struct epoll_event;


namespace muduo
{
namespace net
{


///
/// IO Multiplexing with epoll(4). 继承于抽象类Poller,子类必须实现其接口函数poll, updateChannel, removeChannel
///
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;//一个socketfd对应一个struct epoll_event
  typedef std::map<int, Channel*> ChannelMap;    //key=socketfd value=Channel


  int epollfd_;
  EventList events_;
  ChannelMap channels_;
};


}
}
#endif  // MUDUO_NET_POLLER_EPOLLPOLLER_H


EPollPoller.cc

#include <muduo/net/poller/EPollPoller.h>


#include <muduo/base/Logging.h>
#include <muduo/net/Channel.h>


#include <boost/static_assert.hpp>


#include <assert.h>
#include <errno.h>
#include <poll.h>
#include <sys/epoll.h>


using namespace muduo;
using namespace muduo::net;


// On Linux, the constants of poll(2) and epoll(4)
// are expected to be the same.
BOOST_STATIC_ASSERT(EPOLLIN == POLLIN);
BOOST_STATIC_ASSERT(EPOLLPRI == POLLPRI);
BOOST_STATIC_ASSERT(EPOLLOUT == POLLOUT);
BOOST_STATIC_ASSERT(EPOLLRDHUP == POLLRDHUP);
BOOST_STATIC_ASSERT(EPOLLERR == POLLERR);
BOOST_STATIC_ASSERT(EPOLLHUP == POLLHUP);


namespace
{
const int kNew = -1;
const int kAdded = 1;
const int kDeleted = 2;
}


/*
创建epollfd_, 
可以关注的最多事件数目kInitEventListSize

*/
EPollPoller::EPollPoller(EventLoop* loop)
  : Poller(loop),
    epollfd_(::epoll_create1(EPOLL_CLOEXEC)),
    events_(kInitEventListSize)
{
  if (epollfd_ < 0)
  {
    LOG_SYSFATAL << "EPollPoller::EPollPoller";
  }
}


EPollPoller::~EPollPoller()
{
  ::close(epollfd_);
}




/*poll
等待事件发生
有numEvents 个描述符发生事件
从events_[i].data.ptr 中获取该描述符对应的Channel
将Channel 的revents_设为events_[i].events,
最后将其放入activeChannels

*/
Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels)
{
  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";
    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;
}


/*
有numEvents 个描述符发生事件
从events_[i].data.ptr 中获取该描述符对应的Channel
将Channel 的revents_设为events_[i].events,
最后将其放入activeChannels
*/
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);
  }
}




/*updateChannel
根据channel->index()知道
是新的连接,先看key=socketfd 在channels_ 中是否存在,存在则
让event.data.ptr 保存channel; 并将该socketfd 加入epollfd_集合;
老的连接则将该socketfd 从epollfd_集合修改或者删除

*/
void EPollPoller::updateChannel(Channel* channel)
{
  Poller::assertInLoopThread();
  LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();
  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);
  }
  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);
      channel->set_index(kDeleted);
    }
    else
    {
      update(EPOLL_CTL_MOD, channel);
    }
  }
}


/*updateChannel
根据channel->index()知道是否要删除
先看key=socketfd 在channels_ 中是否存在,存在则先将其删除,
再将该socketfd 从epollfd_集合中删除

*/
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);
}




/*
将channel 赋给该socketfd的event.data.ptr = channel;
并在epollfd_集合中对socketfd 进行operation 操作
*/
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;
    }
  }
}

总结:EPollPoller 类无非是对 ChannelMap channels_;的增删以及epoll集合的增删,还有就是将发生事件的Channel放入传入的参数为activeChannels中。

因为在EventLoop::EventLoop()中对象指针Poller指向了新建的EPollPoller 类对象,因此EventLoop类中会调用EPollPoller 类中实现的函数的,那么在哪里调用的呢?

int main(int argc, char* argv[])
{
  int numThreads = 0;
  if (argc > 1)
  {
    benchmark = true;
    Logger::setLogLevel(Logger::WARN);
    numThreads = atoi(argv[1]);
  }
  EventLoop loop;
  HttpServer server(&loop, InetAddress(8000), "dummy");
  server.setHttpCallback(onRequest);
  server.setThreadNum(numThreads);
  server.start();
  loop.loop(); //该函数会告诉答案的
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 当使用 epoll 进行网络编程时,如果有新的连接请求到达,可以通过监听套接字(listening socket)上的 EPOLLIN 事件来处理。此时可以通过 accept() 函数接收新的连接,并将新的连接加入到 epoll 实例中进行监听。 具体地,可以先创建一个监听套接字,然后将该套接字添加到 epoll 实例中进行监听,如下所示: ```c int listen_fd = socket(AF_INET, SOCK_STREAM, 0); // 设置 listen_fd 为非阻塞模式 int flags = fcntl(listen_fd, F_GETFL, 0); fcntl(listen_fd, F_SETFL, flags | O_NONBLOCK); struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(8080); bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)); listen(listen_fd, SOMAXCONN); int epoll_fd = epoll_create1(0); struct epoll_event event; event.events = EPOLLIN | EPOLLET; event.data.fd = listen_fd; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event); ``` 上述代码中,我们首先创建了一个监听套接字 listen_fd,并将其设置为非阻塞模式。接着,我们设置了服务器地址、端口,绑定套接字并开始监听。然后,我们创建了一个 epoll 实例 epoll_fd,并将监听套接字的文件描述符添加到 epoll 实例中进行监听。 当有新的连接请求到达时,epoll_wait() 函数会返回并通知我们监听套接字上有 EPOLLIN 事件发生。此时,我们可以通过 accept() 函数接收新的连接,并将新的连接的文件描述符添加到 epoll 实例中进行监听,如下所示: ```c struct epoll_event events[MAX_EVENTS]; while (1) { int ready_fds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); for (int i = 0; i < ready_fds; ++i) { if (events[i].data.fd == listen_fd) { // 新的连接请求 while (1) { struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); int client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_len); if (client_fd < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { break; // 没有更多的连接请求 } else { perror("accept error"); break; } } // 将新的连接设置为非阻塞模式 flags = fcntl(client_fd, F_GETFL, 0); fcntl(client_fd, F_SETFL, flags | O_NONBLOCK); // 将新的连接加入到 epoll 实例中进行监听 event.events = EPOLLIN | EPOLLET; event.data.fd = client_fd; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event); } } else { // 可读事件 // 处理读取数据的操作 } } } ``` 上述代码中,我们通过循环调用 epoll_wait() 函数来等待事件发生,并处理事件。当有新的连接请求到达时,我们在 while 循环中使用 accept() 函数接收新的连接,并将新的连接设置为非阻塞模式。然后,我们将新的连接的文件描述符添加到 epoll 实例中进行监听。 需要注意的是,在设置监听套接字和新的连接的文件描述符时,我们都将其设置为非阻塞模式。这是因为在使用 epoll 时,所有的 IO 操作都应该是非阻塞的,以便更好地利用系统资源和提高程序的并发度。 ### 回答2: epoll是一种在Linux操作系统中用于高效处理大量文件描述符的机制。当对方新发起连接时,epoll会按照以下步骤处理: 1. 创建socket并进行绑定、监听操作。 2. 注册监听的文件描述符(socket)到epoll实例中,使用epoll_ctl函数进行注册。 3. 使用epoll_wait函数监听文件描述符的事件。 4. 当对方发起新连接时,epoll_wait会返回对应的事件,判断该事件为连接就绪(EPOLLIN),则执行下一步操作。 5. 调用accept函数接受对方的连接请求,创建一个新的socket用于和对方进行通信。 6. 将新的socket注册到epoll实例中,使用epoll_ctl函数进行注册。 7. 继续监听文件描述符的事件,等待可读或可写事件的发生。 8. 当文件描述符上发生可读或可写事件时,使用read或write函数进行数据的读取或写入操作。 9. 处理完读写操作后,继续监听文件描述符的事件。 总结来说,epoll处理对方新发起的连接的过程是先注册监听的文件描述符,当对方新发起连接时,epoll会返回连接就绪事件,然后调用accept函数创建新的socket用于通信,并将新的socket注册到epoll实例中,最后继续监听该socket的事件,进行数据的读写操作。 ### 回答3: 在epoll的工作机制中,当对方新发起连接时,epoll会通过事件通知机制通知我们。具体而言,我们需要对这种新的连接事件进行处理,以便能够与对方建立通信。 在处理对方新发起连接时,一般会经历以下步骤: 1. 监听套接字就绪:首先,我们需要将服务器的监听套接字(一般是通过socket函数创建的)加入到epoll的事件集合中,并指定关注的事件类型,如EPOLLIN(可读事件)或EPOLLERR(错误事件)等。 2. 等待事件就绪:然后,我们通过epoll_wait函数等待事件就绪,该函数会阻塞,直到事件发生或达到超时时间。 3. 处理就绪事件:当新连接事件就绪时,我们可以使用accept函数接收该连接,并创建新的套接字,用于后续通信。同时,我们可以将新创建的套接字也添加到epoll的事件集合中,以便对其进行后续操作,如读取或写入数据。 需要注意的是,处理对方新发起连接事件并建立通信并不仅限于这些步骤,具体的处理流程还会根据具体的需求和设计来确定。此外,在实际应用中,我们还需要处理并发连接的情况,可以使用多线程或多进程来实现。可以利用epoll的水平触发模式(EPOLLLT)或边缘触发模式(EPOLLET)来实现。 综上所述,epoll处理对方新发起连接时,会通知我们该事件的就绪状态,并提供接口供我们处理该事件,使我们能够与对方建立通信。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值