高级IO_多路转接之epoll


前言

今天我们所要学习的epoll作为多路转接的后起之秀,会弥补之前所讲的select和poll的所有缺点,可以说从思想上,epoll与select、poll完全不同。


一、epoll接口

int epoll_create(int size);

  • 作用:创建epoll并返回创建的epoll的文件描述符。
  • 参数int size:epoll可以管理的fd数量。
  • 返回值:会返回一个文件描述符,这是epoll的fd。 从这里就可以看出epoll的不同点。

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

  • 作用:根据op操作选项对fd进行内核态的控制。
  • 参数int epfd:就是epoll_create创建的epoll的文件描述符。
  • 参数int op,有三种操作选项EPOLL_CTL_ADD、EPOLL_CTL_MOD、EPOLL_CTL_DEL。分别对应想epoll添加要关心的fd和其对应事件、修改fd要关心的对应事件、删除epoll要关心的fd。
  • 参数struct epoll_event *event,该结构体包含两个成员,events就是要关心的事件,data是一个联合体我们今天使用fd就可以。
  • 返回值:成功为0,失败为-1.
    在这里插入图片描述

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

这个函数的功能就与select、poll一样了。

  • 参数int epfd:就是epoll_create创建的epoll的文件描述符。
  • 参数struct epoll_event *events:传的是一个数组,同时也是一个输出型参数,用来保存从已就绪队列中拿出的就绪事件。
  • 参数int maxevents:events的元素数量。
  • 参数int timeout:与select、poll的timeout功能一致。
  • 返回值:返回已就绪事件的个数,若timeout则返回0,出错误返回-1。

二、如何理解epoll

epoll与select、poll有很大的不同。
首先在创建epoll时,直接给epoll创建了一个文件描述符,我们就可以将epoll视为一个文件,其对应的struct file里就有指针指向一个独特的epoll数据结构!(内核中)

该数据结构如下

在这里插入图片描述
它最重要的是两个部分,这两个部分分别是一颗红黑树,一个队列。
这棵红黑树用来保存所有需要关心的fd和其对应事件,当驱动层(有可能是键盘驱动,也有可能是网卡驱动)有数据就绪时,会通过设置好的callback回调函数,先将数据向上交付,再去查找这棵rb_tree有没有对应的fd和关心事件,如果有,则构建新节点将就绪事件插入到就绪队列当中,最后通过epoll_wait中的输出型参数events将事件从内核层的就绪队列拿到用户层。

三、epoll示例代码

Epoll.hpp
#include "log.hpp"
#include <sys/epoll.h>
#include <functional>
#include <memory>

#define MAX_EPOLLFDS 1024

class Epoller
{
public:
    Epoller() {}

    void Init()
    {
        _epfd = epoll_create(MAX_EPOLLFDS);
        if (_epfd < 0)
        {
            lg(Fatal, "Epoll Create Error...");
            exit(1);
        }
        else
        {
            lg(Info, "Epoll Create Succeed, epfd:%d", _epfd);
        }
    }

    int EpollWait(epoll_event *events, int num, int timeout)
    {
        int n = epoll_wait(_epfd, events, num, timeout);
        return n;
    }

    void EpollerUpdate(int op, int fd, uint32_t events)
    {
        if (op == EPOLL_CTL_DEL)
        {
            int n = epoll_ctl(_epfd, op, fd, nullptr);
            if (n < 0)
            {
                lg(Warning, "Epoll Delete Error, fd:%d", fd);
                return;
            }
            else
            {
                lg(Info, "Epoll Delete Succeed, fd:%d", fd);
                return;
            }
        }
        struct epoll_event tmp;
        tmp.data.fd = fd;
        tmp.events = events;
        int n = epoll_ctl(_epfd, op, fd, &tmp);
        if (n < 0)
        {
            lg(Warning, "Epoll Control Error, fd:%d", fd);
        }
        else
        {
            lg(Info, "Epoll Control Succeed, fd:%d", fd);
        }
    }
    ~Epoller()
    {
        if (_epfd != -1)
        {
            close(_epfd);
        }
    }

private:
    int _epfd;
};
EpollServer.hpp
#include "Socket.hpp"
#include "Epoll.hpp"

#define EVENT_IN EPOLLIN

const std::string default_ip = "0.0.0.0";
const uint16_t default_port = 8080;

class EpollServer
{
    static const int num = 128;

public:
    EpollServer(uint16_t port = default_port)
        : _port(port), _listensock(new Socket), _epoller(new Epoller) {}

    void Init()
    {
        _epoller->Init();
        _listensock->Init();
        _listensock->Bind(AF_INET, default_ip, _port);
        _listensock->Listen();
        _epoller->EpollerUpdate(EPOLL_CTL_ADD, _listensock->_sockfd, EVENT_IN);
    }

    void Accepter()
    {
        int newfd = _listensock->Accept();
        if (newfd == -1)
            return;
        _epoller->EpollerUpdate(EPOLL_CTL_ADD, newfd, EVENT_IN);
    }

    void Handler(int fd)
    {
        char buffer[1024];
        memset(buffer, 0, sizeof buffer);
        int n = read(fd, buffer, sizeof buffer - 1);
        if (n > 0)
        {
            buffer[n] = 0;
            std::string mes = buffer;
            std::cout << mes;
        }
        else if (n < 0)
        {
            lg(Warning, "Read Error...");
            _epoller->EpollerUpdate(EPOLL_CTL_DEL, fd, 0);
            close(fd);
        }
        else
        {
            lg(Info, "Foreign Host Closed...");
            _epoller->EpollerUpdate(EPOLL_CTL_DEL, fd, 0);
            close(fd);
        }
    }

    void Start()
    {
        struct epoll_event events[num];
        while (1)
        {
            int n = _epoller->EpollWait(events, num, 5000);
            if (n > 0)
            {
                for (int i = 0; i < n; ++i)
                {
                    if (events->data.fd == _listensock->_sockfd)
                    {
                        // accept
                        Accepter();
                        continue;
                    }
                    Handler(events->data.fd);
                }
            }
        }
    }

    ~EpollServer()
    {
        _listensock->Close();
    }

private:
    std::unique_ptr<Socket> _listensock;
    uint16_t _port;
    std::unique_ptr<Epoller> _epoller;
};

在这里插入图片描述

  • 24
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风君子吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值