Linux高级IO之poll与epoll

news2024/9/28 23:30:17

文章目录

    • poll
      • 使用
    • epoll
      • 系统调用
    • epoll的工作原理
      • 红黑树
      • 队列
    • epoll的工作模式
      • 水平触发
      • 边缘触发
    • Reactor设计模式
      • 工作原理
      • epoll Reactor设计模式的简单示例

poll和epoll都是多路转接的调用,但是epoll实在过于优秀了,一般也都是用epoll的,除此之外还着使用时还蕴含着Reactor设计模式的思想

poll

poll几乎是解决了select的痛点问题的,就像c和c with class一样

使用

poll的函数原型和数据结构长这样

#include <poll.h>

int poll(struct pollfd *fd, nfds_t nfds, int tiemout);

struct pollfd{
    int fd;			// 文件描述符
    short events;	// 时间类型
    short revents;	// 实际发生的时间
};
  • fds:只想一个pollfd结构体数组的指针,每一个结构体都在监视一个文件描述符
  • nfds:文件描述符的数量
  • timeout:与select相同,只是直接使用int作为类型,单位是毫秒

epoll

epoll是为了处理大量句柄而做了改进的poll,在实际中运用的最多的也是这个

系统调用

这个系统调用是用于创建一个epoll模型的

这个系统调用是用于设置监听的文件描述符和事件

最后一个epoll_event是这样的

struct epoll_event{
    uint32_t events;
    epoll_data_t data;
};

events表示事件发生时要做的操作,也就是需要监听的事件

data则是监听对应的文件描述符

events也是一个位图,使用宏来表示事件

  • EPOLLIN:表示对应的文件描述符可以读
  • EPOLLOUT:表示对应的文件描述符可以写
  • EPOLLRI:表示对应的文件描述符有紧急数据可读
  • EPOLLRR:表示文件描述符出错
  • EPOLLHUP:表示文件描述符被挂断
  • EPOLLET:表示设为边缘出发模式
  • EPOLLONESHOT:只监听一次事件,如果需要继续监听,需要重新加入EPOLL队列中

epoll_wait是用于等待是否就绪

下面是一个简单的epoll示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#define MAX_EVENTS 10
#define TIMEOUT 5000  // 5秒

int main() {
    int epfd = epoll_create1(0);
    if (epfd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }

    // 假设我们要监听标准输入(fd = 0)
    struct epoll_event ev;
    ev.events = EPOLLIN;  // 监听读事件
    ev.data.fd = STDIN_FILENO;

    if (epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1) {
        perror("epoll_ctl: stdin");
        close(epfd);
        exit(EXIT_FAILURE);
    }

    struct epoll_event events[MAX_EVENTS];
    int nfds;

    while (1) {
        nfds = epoll_wait(epfd, events, MAX_EVENTS, TIMEOUT);
        if (nfds == -1) {
            if (errno == EINTR)
                continue;  // 被信号中断,重新调用
            perror("epoll_wait");
            break;
        }

        if (nfds == 0) {
            printf("等待超时,没有事件发生。\n");
            continue;
        }

        for (int i = 0; i < nfds; ++i) {
            if (events[i].data.fd == STDIN_FILENO) {
                if (events[i].events & EPOLLIN) {
                    char buffer[1024];
                    ssize_t count = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
                    if (count == -1) {
                        perror("read");
                    } else if (count == 0) {
                        printf("标准输入关闭。\n");
                        close(epfd);
                        exit(EXIT_SUCCESS);
                    } else {
                        buffer[count] = '\0';
                        printf("读取到数据: %s", buffer);
                    }
                }
            }
            // 这里可以处理其他文件描述符的事件
        }
    }

    close(epfd);
    return 0;
}

epoll的工作原理

epoll的内核中使用了两个数据结构来管理文件描述符,分别是一个红黑树,一个队列

红黑树

红黑树主要用于存放所有正在监视的文件描述符,当我们使用epoll_ctl设置关心的事件,就是在这个红黑树上进行增删改

队列

队列存放的是就绪事件的文件描述符,也就是epoll_wait所等待的就绪的文件描述符

用户使用的门槛其实降低了很多,只需要设置监视,然后获取结果,不需要对fd和event进行管理

每一个epoll对象都有一个独立的eventpoll结构体,用来存放通过ctl向epoll对象添加的事件

事件会放在红黑树,因此插入的时间是O(lg n)

添加到epoll到事件会与设备建立回调关系,当事件发生,调用这个回调方法

这个回调方法会将发生的事件放在rdlist双联白哦

而每一个事件都对应着一个epitem结构体

里面是这样的

struct epitem{
    struct rb_node rbn; 		// 红黑树节点
    struct list_head rdllink; 	// 双向链表节点
    struct epoll_filefd ffd; 	// 事件句柄信息
    struct eventpoll *ep;		// 只想其所属的eventpoll对象
    struct epoll_event event;	// 期待发生的事件类型
};

当epoll_wait检查事件是否发生时,只需要查看epitem的rdlist是否为空就绪,这个事件复杂度就只有O(1)

epoll的工作模式

epoll有两种工作模式

一种是水平触发LT,另一种是边缘触发ET

水平触发

水平触发就可以理解为阻塞的触发,如果事件发生了,那就会一直进行等待并且通知

epoll的默认工作模式就是水平触发,当epoll检测到事件就绪时,可以不立即进行处理,或者仅处理一部分,一直到缓冲区的所有数据都被处理完,才不会立即返回,支持阻塞和非阻塞

但是这样做的代价很高,因为不能处理返回其他事情

边缘触发

边缘触发就是类似于非阻塞的情况,事件发生时,只通知一次,爱拿不拿,不保证数据依然还在,你只有一次处理机会

ET的性能会比LT高很多,而Nginx默认采用的就是ET模式

但是ET只支持非阻塞

Reactor设计模式

reactor是一种用于处理事件的设计模式,核心思想就是将事件和处理分开

用一个事件的循环来监控多个IO事件,当事件发生时,reactor会调用相应的处理器(回调函数)来处理这些事件

工作原理

一般就是分成四个逻辑

  1. 注册事件,将事件源和处理器注册到事件循环中
  2. 等待事件,事件循环持续监控事件源,等待事件发生
  3. 分发事件,事件发生,事件循环调用处理器
  4. 处理事件,处理器执行具体到业务逻辑

应用场景,一般就是高并发服务器(HTTP服务器),图形用户界面,网络通信

epoll Reactor设计模式的简单示例

#include <iostream>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
#include <unordered_map>

const int MAX_EVENTS = 10;

// 事件处理器基类
class EventHandler {
public:
    virtual void handleEvent(uint32_t events) = 0;
};

// Reactor类
class Reactor {
public:
    Reactor() {
        epoll_fd = epoll_create1(0);
        if (epoll_fd == -1) {
            std::cerr << "Failed to create epoll file descriptor" << std::endl;
            exit(EXIT_FAILURE);
        }
    }

    ~Reactor() {
        close(epoll_fd);
    }

    // 注册事件处理器
    void registerHandler(int fd, EventHandler* handler, uint32_t events) {
        struct epoll_event ev;
        ev.events = events;
        ev.data.fd = fd;

        if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
            std::cerr << "Failed to add file descriptor to epoll" << std::endl;
            exit(EXIT_FAILURE);
        }

        handlers[fd] = handler;
    }

    // 运行事件循环
    void run() {
        struct epoll_event events[MAX_EVENTS];
        while (true) {
            int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
            if (n == -1) {
                std::cerr << "epoll_wait failed" << std::endl;
                exit(EXIT_FAILURE);
            }

            for (int i = 0; i < n; ++i) {
                int fd = events[i].data.fd;
                uint32_t event_types = events[i].events;
                if (handlers.count(fd)) {
                    handlers[fd]->handleEvent(event_types);
                }
            }
        }
    }

private:
    int epoll_fd;
    std::unordered_map<int, EventHandler*> handlers;
};

// 自定义事件处理器
class MyEventHandler : public EventHandler {
public:
    void handleEvent(uint32_t events) override {
        if (events & EPOLLIN) {
            char buffer[1024];
            ssize_t count = read(STDIN_FILENO, buffer, sizeof(buffer));
            if (count == -1) {
                std::cerr << "Read error" << std::endl;
            } else if (count == 0) {
                std::cout << "EOF" << std::endl;
            } else {
                std::cout << "Read: " << std::string(buffer, count) << std::endl;
            }
        }
    }
};

int main() {
    Reactor reactor;
    MyEventHandler handler;

    // 将标准输入(STDIN_FILENO)注册到epoll中,监听读事件(EPOLLIN)
    reactor.registerHandler(STDIN_FILENO, &handler, EPOLLIN);

    // 开始事件循环
    reactor.run();

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2171128.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mysql索引结构操作(主键/唯一键/普通索引的创建/查询/删除),复合索引介绍(索引覆盖,索引最左匹配原则)

目录 索引操作 创建索引 主键索引 介绍 在创建表时设置主键 创建表后添加主键 唯一键索引 介绍 在创建表时设置唯一键 创建表后添加唯一键 普通索引 在创建表时指定某列为索引 创建表后添加普通索引 自主命名索引 索引创建原则 哪些列适合创建索引 不适合作为…

猫头虎带你解决:error Error: certificate has expired

&#x1f42f;猫头虎带你解决&#xff1a;error Error: certificate has expired &#x1f4a5; 今天有粉丝问猫哥&#xff1a;“&#x1f42f;猫头虎&#xff0c;我在 Node.js 项目中使用 Yarn 安装包时遇到了一个错误&#xff1a;Error: certificate has expired。你能帮忙解…

【机器学习(十二)】机器学习回归案例之二手汽车价格预测—XGBoost回归算法—Sentosa_DSML社区版

文章目录 一、算法和背景介绍二、Python代码和Sentosa_DSML社区版算法实现对比(一) 数据读入与统计分析(二) 数据处理(三) 特征选择与相关性分析(四) 样本分区与模型训练(五) 模型评估和模型可视化 三、总结 一、算法和背景介绍 关于XGBoost的算法原理&#xff0c;已经进行了介…

[C++]栈队列改成模板类

栈、队列都更改成模板类 栈 .hpp #ifndef MY_STACK_H #define MY_STACK_H #include <iostream>using namespace std;template<typename T> class my_stack { private:T *base;//动态栈指针int top;//栈顶元素int size;//栈大小 public:my_stack();//无参构造my_…

宠物空气净化器有必要买吗?希喂、霍尼韦尔和352哪款更推荐?

国庆假终于要来了&#xff0c;对于我这个上班族而言&#xff0c;除了春节假期最期待的就是这个国庆假&#xff0c;毕竟假期这么长&#xff0c;家里还有一只小猫咪&#xff0c;一直都没时间陪它&#xff0c;终于给我找到时间带它会老家玩一趟了。 我跟我妈说的时候&#xff0c;…

Apache OFBiz SSRF漏洞CVE-2024-45507分析

Apache OFBiz介绍 Apache OFBiz 是一个功能丰富的开源电子商务平台&#xff0c;包含完整的商业解决方案&#xff0c;适用于多种行业。它提供了一套全面的服务&#xff0c;包括客户关系管理&#xff08;CRM&#xff09;、企业资源规划&#xff08;ERP&#xff09;、订单管理、产…

Vulhub TheEther_1.0.1靶机详解

项目地址 https://download.vulnhub.com/theether/theEther_1.0.1.zip实验过程 将下载好的靶机导入到VMware中&#xff0c;设置网络模式为NAT模式&#xff0c;然后开启靶机虚拟机 使用nmap进行主机发现&#xff0c;获取靶机IP地址 nmap 192.168.47.1-254根据对比可知theEthe…

【 EXCEL 数据处理 】000003 案列 标记涨跌,保姆级教程。使用的软件是微软的Excel操作的。处理数据的目的是让数据更直观的显示出来,方便查看。

【 EXCEL 数据处理 】000003 案列 使用条件格式之大于和小于&#xff0c;标记涨跌&#xff0c;保姆级教程。使用的软件是微软的Excel操作的。处理数据的目的是让数据更直观的显示出来&#xff0c;方便查看。 &#x1f4da;一、直接上案例 &#x1f4d6;1.使用条件格式之大于和小…

CMU 10423 Generative AI:lec7、8、9(专题2:一张图理解diffusion model结构、代码实现和效果)

本文介绍diffusion model是什么&#xff08;包括&#xff1a;模型详细的架构图、各模块原理和输入输出、训练算法解读、推理算法解读&#xff09;、以及全套demo代码和效果。至于为什么要这么设计、以及公式背后的数学原理&#xff0c;过程推导很长很长&#xff0c;可见参考资料…

Bug:ThreadPoolTaskScheduler搭配CronTask完成定时任务,关闭scheduler后CronTask任务仍然执行?

【问题】执行下面代码后&#xff0c;关闭ThreadPoolTaskScheduler&#xff0c;CronTask仍然继续执行。 Configuration public class config {Beanpublic String getString() throws InterruptedException {Runnable runnable () -> {try {System.out.println("hello r…

动态规划算法:13.简单多状态 dp 问题_打家劫舍II_C++

目录 题目链接&#xff1a;LCR 090. 打家劫舍 II - 力扣&#xff08;LeetCode&#xff09; 一、题目解析 题目&#xff1a; 解析&#xff1a; 二、算法原理 1、状态表示 2、状态转移方程 状态转移方程推理&#xff1a; 1、i位置状态分析 2、首尾状态分析 3、初始化 d…

Meta震撼发布Llama3.2大规模模型

在2024.9.26的年Meta Connect大会上&#xff0c;Meta正式推出了Llama3.2模型&#xff0c;旨在提升边缘AI和视觉任务的能力。Llama3.2系列包括11亿和90亿参数的中型视觉模型&#xff0c;以及为移动设备优化的1亿和3亿参数的小型模型&#xff0c;并针对高通和联发科的硬件平台进行…

Webpack 介绍

Webpack 介绍 Date: August 29, 2024 全文概要 Webpack概念&#xff1a; Webpack是一个静态的模块化的打包工具&#xff0c;可以为现代的 JavaSript 应用程序进行打包。 1-静态&#xff1a;Webpack可以将代码打包成最终的静态资源 2-模块化&#xff1a;webpack支持各种模块…

教师工作量评估与管理软件

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

Spring异常处理-@ExceptionHandler-@ControllerAdvice-全局异常处理

文章目录 ResponseBodyControllerAdvice最终的异常处理方式 异常的处理分两类 编程式处理&#xff1a;也就是我们的try-catch 声明式处理&#xff1a;使用注解处理 ResponseBody /*** 测试声明式异常处理*/ RestController public class HelloController {//编程式的异常处理&a…

EasyAR自定义相机RTSP视频流(CustomCamera)

EasyAR可以使用视频源作为输入源&#xff0c;官方给出了示例和文档&#xff0c;但是对于大部分Unity开发人员来说看了文档还是一头雾水。 在Android Studio中将custom-camera.jar添加libs中&#xff0c;就可以查看源代码了 分析其源代码&#xff0c;主要是ExternalCameraSampl…

【linux 多进程并发】linux下使用常见命令,来解析进程家族体系脉络

0101 Linux进程 ​专栏内容&#xff1a; postgresql使用入门基础手写数据库toadb并发编程 个人主页&#xff1a;我的主页 管理社区&#xff1a;开源数据库 座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物. 文章目录 0101 Li…

ASP.NET Core 打包net8.0框架在Linux CentOS7上部署问题

问题1 libstdc.so.6版本过低。 CentOS7默认安装的gcc版本太低&#xff0c;达不到.net8的启动条件。 /lib64/libstdc.so.6: version GLIBCXX_3.4.20’ not found (required by ./IDT_net) /lib64/libstdc.so.6: version GLIBCXX_3.4.21’ not found (required by ./IDT_net) 解…

恢复丢失的数据:恢复数据库网络解决方案

探索恢复数据库网络的深度对于了解现代企业如何防御其数据不断增长的威胁至关重要。在一个时代&#xff0c;数字证据和取证网络安全在法律和商业领域扮演关键角色&#xff0c;这些网络提供的弹性是不可或缺的。深入研究恢复数据库网络的重要性不仅仅是数据保护&#xff0c;它还…

ubuntu安装mysql 8,mysql密码的修改

目录 1.安装mysql 82.查看当前状态3.手动给数据库设置密码mysql5mysql8 4.直接把数据库验证密码的功能关闭掉 1.安装mysql 8 apt install mysql-server-8.0敲 Y 按回车 table 选ok 2.查看当前状态 service mysql status显示active&#xff08;running&#xff09;证明安装成…