栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

Linux系统编程—epoll

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Linux系统编程—epoll

1. 简介

epoll 的优点:

  • 当检查大量的文件描述符时,epoll 的性能比 select() 和 poll() 要高。
  • epoll 既支持水平触发也支持边缘触发,select() 和 poll() 只支持水平触发。

epoll 的缺点:epoll 是 Linux 特有的,不可移植。

2. 函数原型
#include 

int epoll_create(int size);
  • 创建一个新的 epoll 实例。size 参数告诉内核应如何为内部数据结构划分初始大小,自 Linux 2.6.8 以后,size 参数被忽略不用。
  • 出错时返回 -1,成功时返回 epoll 实例的文件描述符,当这个文件描述符不再需要时,应通过 close() 来关闭它。
#include 

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  • 修改文件描述符 epfd 所指代的 epoll 实例的兴趣列表。

  • 成功时返回 0,出错时返回 -1。

  • fd 指明了要修改兴趣列表中的哪一个文件描述符的设置,它可以是管道、FIFO、套接字、POSIX 消息队列、inotify 实例、另一个 epoll 实例的文件描述符,但不能是普通文件或目录。

  • op 指明了要执行何种操作,可以为:

    • EPOLL_CTL_ADD:添加 fd 至兴趣列表中去,感兴趣的事件由 event 参数指定;兴趣列表中必须事先不存在 fd。
    • EPOLL_CTL_MOD:修改 fd 的感兴趣事件为 event 中指定的值,fd 必须已存在于兴趣列表中。
    • EPOLL_CTL_DEL:从兴趣列表中移除 fd,忽略 event 参数;fd 必须已存在于兴趣列表中。
  • event 参数指定了感兴趣的事件:

    typedef union epoll_data {
       void        *ptr;
       int          fd;
       uint32_t     u32;
       uint64_t     u64;
    } epoll_data_t;
    
    struct epoll_event {
       uint32_t     events;      
       epoll_data_t data;        
    };
    
    • epoll_event 中的 events 字段指定了要监听哪些事件,data 字段用于指定当事件就绪时回传给调用进程的信息。
    • 常用的事件如下:
      • EPOLLIN:读就绪;
      • EPOLLOUT:写就绪;
      • EPOLLRDHUP:流套接字的对端关闭或关闭了写端;
      • EPOLLERR:有错误发生,总是会监听此类事件,无需手动指定;
      • EPOLLHUP:出现挂断,总是会监听此类事件,无需手动指定;
      • EPOLLET:使用边缘触发;
      • EPOLLONESHOT:一次性检查,即完成通知后禁用检查;
#include 

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
  • 保持阻塞,直到有事件就绪或超时。
  • 出错时返回 -1,超时时返回 0,否则返回就绪的文件描述符个数。
  • events 是一个传入传出参数,其大小为 maxevents,调用返回时,其保存了所有已就绪的文件描述符及相应的就绪事件。
  • timeout 用于指定超时时间,以毫秒为单位,-1 表示一直阻塞,0 表示执行一次非阻塞查询。
  • 可以在一个线程中调用 epoll_ctl(),另一个线程中执行 epoll_wait()。
3. 例子
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

static uint16_t Port = 6666;
static const char* IP = "127.0.0.1";
static std::string Msg("Hello, World!");
static constexpr size_t MSG_LEN = 13;
static std::mutex ioLock;

class IOManager {
public:
    IOManager(int listenFd=-1, int size=4);
    ~IOManager();

    void run();
    void add_read(int fd);
    void add_write(int fd);
    void del(int fd);

private:
    int m_epFd;
    int m_listenFd;
    int m_size;
    struct epoll_event* m_eventList;
};

IOManager::IOManager(int listenFd, int size): m_listenFd(listenFd), m_size(size), m_eventList(nullptr) {
    m_epFd = epoll_create(m_size);
    m_eventList = new struct epoll_event[m_size];

    if (m_listenFd >= 0) {
        add_read(m_listenFd);
    }
}

IOManager::~IOManager() {
    if (m_eventList) {
        delete [] m_eventList;
    }
    if (m_epFd >= 0) {
        close(m_epFd);
    }
}

void IOManager::add_read(int fd) {
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = fd;

    epoll_ctl(m_epFd, EPOLL_CTL_ADD, fd, &ev);
}

void IOManager::add_write(int fd) {
    struct epoll_event ev;
    ev.events = EPOLLOUT;
    ev.data.fd = fd;

    epoll_ctl(m_epFd, EPOLL_CTL_MOD, fd, &ev);
}

void IOManager::del(int fd) {
    epoll_ctl(m_epFd, EPOLL_CTL_DEL, fd, NULL);
}

void IOManager::run() {
    int nReady;
    char buf[MSG_LEN];
    int fd;
    uint32_t events;

    while (true) {
        nReady = epoll_wait(m_epFd, m_eventList, m_size, -1);
        if (nReady == -1) {
            perror("epoll_wait()");
            exit(EXIT_FAILURE);
        }

        for (int i = 0; i < nReady; i++) {
            fd = m_eventList[i].data.fd;
            events = m_eventList[i].events;

            if (fd == m_listenFd) {
                int connFd = accept(m_listenFd, NULL, NULL);
                if (connFd == -1) {
                    perror("accept()");
                } else {
                    add_read(connFd);
                }
            } else if (events & EPOLLIN) {
                read(fd, buf, MSG_LEN);
                add_write(fd);
            } else if (events & EPOLLOUT) {
                write(fd, Msg.data(), MSG_LEN);
                del(fd);
                close(fd);
            }

            if (events & (EPOLLERR | EPOLLRDHUP | EPOLLHUP)) {
                del(fd);
                close(fd);
            }
        }
    }
}

void server() {
    int listenSock = socket(AF_INET, SOCK_STREAM, 0);
    int value = 1;
    setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int));

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_port = htons(Port);
    addr.sin_family = AF_INET;
    inet_pton(AF_INET, IP, &addr.sin_addr);

    bind(listenSock, (const struct sockaddr*)&addr, sizeof(addr));
    listen(listenSock, 4);

    IOManager manager(listenSock);
    manager.run();
}

void client() {
    using namespace std::literals;

    std::this_thread::sleep_for(1s);

    int sock = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_port = htons(Port);
    addr.sin_family = AF_INET;
    inet_pton(AF_INET, IP, &addr.sin_addr);

    connect(sock, (const struct sockaddr*)&addr, sizeof(addr));

    char buf[MSG_LEN+1] = {0};
    ssize_t n = write(sock, Msg.c_str(), MSG_LEN);
    read(sock, buf, n);

    std::lock_guard lock(ioLock);
    std::cout << "client: " << buf << 'n';

    close(sock);
}

int main() {
    constexpr int N = 4;
    for (int i = 0; i < N; i++)
    {
        std::thread t(client);
        t.detach();
    }

    std::thread st(server);
    st.join();

    return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/510619.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号