【面试八股总结】Linux系统下的I/O多路复用

参考资料 :小林Coding、阿秀、代码随想录

        I/O多路复用是⼀种在单个线程或进程中处理多个输入和输出操作的机制。它允许单个进程同时监视多个文件描述符(通常是套接字),一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

        I/O多路复用允许在⼀个线程中处理多个I/O操作,避免了创建多个线程或进程的开销。

一、SELECT

        select是⼀个最古老的I/O多路复⽤机制,它可以监视多个⽂件描述符的可读、可写和错误状态,但是它的效率可能随着监视的文件描述符数量的增加而降低。

实现方式:

        select 将已连接的 Socket 都放到一个文件描述符集合,然后调用 select 函数将文件描述符集合拷贝内核里,让内核来检查是否有事件产生,检查的方式就是通过遍历文件描述符集合的方式,当检查到有事件产生后,将此 Socket 标记为可读或可写, 接着再把整个文件描述符集合拷贝用户态里,然后用户态还需要再通过遍历的方法找到可读或可写的 Socket,然后再对其处理。 

相关函数:

#include <sys/select.h>
int select(int nfds, fd_set* readfds, fd_set* writefds, 
                fd_set* exceptfds, struct timeval* timeout);
- 参数:
    - nfds : 委托内核检测的最大文件描述符的值 + 1
    - readfds : 要检测的文件描述符的读的集合,委托内核检测哪些文件描述符的读的属性
        - 一般检测读操作
        - 对应的是对方发送过来的数据,因为读是被动的接收数据,检测的就是读缓冲区
        - 是一个传入传出参数
    - writefds : 要检测的文件描述符的写的集合,委托内核检测哪些文件描述符的写的属性
        - 委托内核检测写缓冲区是不是还可以写数据(不满的就可以写)
    - exceptfds : 检测发生异常的文件描述符的集合
    - timeout : 设置的超时时间
            struct timeval {
            long tv_sec; /* seconds */
            long tv_usec; /* microseconds */
            };
            - NULL : 永久阻塞,直到检测到了文件描述符有变化
            - tv_sec = 0 tv_usec = 0, 不阻塞
            - tv_sec > 0 tv_usec > 0, 阻塞对应的时间
- 返回值 :
    - -1 : 失败
    - >0(n) : 检测的集合中有n个文件描述符发生了变化
// 将参数文件描述符fd对应的标志位设置为0
void FD_CLR(int fd, fd_set *set);

// 判断fd对应的标志位是0还是1, 返回值 : fd对应的标志位的值,0,返回0, 1,返回1
int FD_ISSET(int fd, fd_set *set);

// 将参数文件描述符fd 对应的标志位,设置为1
void FD_SET(int fd, fd_set *set);

//  fd_set一共有1024 bit, 全部初始化为0
void FD_ZERO(fd_set *set);

缺      点:

  1. 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  2. 同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
  3. select支持的文件描述符数量太小了,默认是1024
  4. fds集合不能重用,每次都需要重置(因为内核会修改发生事件的fd)

二、POLL

     poll是select的⼀种改进,使用轮询方式来检查多个文件描述符的状态,避免了select中文件描述符数量有限的问题。但对于大量的文件描述符,poll的性能也可能变得不够⾼效。

改  进  点:

  1. 基于结构体数组存储要监视的文件描述符,文件描述符数量不受限制,可以处理任意数量的文件描述符;
  2. 结构体中使用revents作为是否发生事件标志,每次遍历只需要将revernts恢复为0,因此文件描述符集合可以重用。
#include <poll.h>
struct pollfd {
    int fd; /* 委托内核检测的文件描述符 */
    short events; /* 委托内核检测文件描述符的什么事件 */
    short revents; /* 文件描述符实际发生的事件 */
};

struct pollfd myfd;
myfd.fd = 5;
myfd.events = POLLIN | POLLOUT;

函数原型:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
- 参数:
        - fds : 是一个struct pollfd 结构体数组,这是一个需要检测的文件描述符的集合
        - nfds : 这个是第一个参数数组中最后一个有效元素的下标 + 1
        - timeout : 阻塞时长
            0 : 不阻塞
            -1 : 阻塞,当检测到需要检测的文件描述符有变化,解除阻塞
            >0 : 阻塞的时长
- 返回值:
        -1 : 失败
        >0(n) : 成功,n表示检测到集合中有n个文件描述符发生变化
    

缺      点:

  1. 每次调用poll时,仍然需要将pollfd集合从用户态拷贝到内核态;
  2. 每次调用poll时,都需要在内核遍历传递进来的所有pollfd。

三、EPOLL

        EPOLL是Linux特有的I/O复用函数。epoll 使用⼀个事件驱动(event-driven)的方式来处理I/O操作,它只会返回就绪的文件描述符,而不是遍历整个文件描述符集合。

        epoll使用一组函数完成任务,而不是一个函数,并且epoll把用户关心的文件描述符上的事件放在内核里的一个事件表中,不需要像select和poll一样每次调用都需要重复传入文件描述符集合。但epoll需要一个额外的文件描述符,用于唯一标识内核中的事件表。

相关函数:

include <sys/epoll.h>
// 创建一个新的epoll实例。在内核中创建了一个数据,这个数据中有两个比较重要的数据,
// 一个是需要检测的文件描述符的信息(红黑树),
// 还有一个是就绪列表,存放检测到数据发送改变的文件描述符信息(双向链表)。
int epoll_create(int size);
    - 参数:
    size : 目前没有意义了。随便写一个数,必须大于0
    - 返回值:
        -1 : 失败
        > 0 : 操作epoll实例的文件描述符
// 对epoll实例进行管理:添加文件描述符信息,删除信息,修改信息
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    - 参数:
        - epfd : epoll实例对应的文件描述符
        - op : 要进行什么操作
            EPOLL_CTL_ADD: 添加
            EPOLL_CTL_MOD: 修改
            EPOLL_CTL_DEL: 删除
        - fd : 要检测的文件描述符
        - event : 检测文件描述符什么事情
                struct epoll_event {
                    uint32_t events; /* Epoll events */
                    epoll_data_t data; /* User data variable */
                };

           常见的Epoll检测事件:    - EPOLLIN    - EPOLLOUT    - EPOLLERR
// 检测函数
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
    - 参数:
        - epfd : epoll实例对应的文件描述符
        - events : 传出参数,保存了发送了变化的文件描述符的信息
        - maxevents : 第二个参数结构体数组的大小
        - timeout : 阻塞时间
            - 0 : 不阻塞
            - -1 : 阻塞,直到检测到fd数据发生变化,解除阻塞
            - > 0 : 阻塞的时长(毫秒)
    - 返回值:
        - 成功,返回发送变化的文件描述符的个数 > 0
        - 失败 -1

LT和ET模式:

        epoll 支持两种事件触发模式,分别是边缘触发(edge-triggered,ET水平触发(level-triggered,LT

  • 使用边缘触发模式时,当被监控的 Socket 描述符上有可读事件发生时,服务器端只会从 epoll_wait 中苏醒一次,即使进程没有调用 read 函数从内核读取数据,也依然只苏醒一次,因此我们程序要保证一次性将内核缓冲区的数据读取完
  • 使用水平触发模式时,当被监控的 Socket 上有可读事件发生时,服务器端不断地从 epoll_wait 中苏醒,直到内核缓冲区数据被 read 函数读完才结束,目的是告诉我们有数据需要读取;

        ET(edge - triggered)是高速工作方式,只支持 no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了。但是请注意,如果一直不对这个 fd 作 IO 操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。

        ET 模式在很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。epoll工作在 ET 模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

EPOLLONESHOT事件:

        即使使用ET模式,一个socket上的某个事件还是可能被触发多次。这在并发程序中会引起一个问题,假设一个线程在读取完某个socket上的数据后开始处理该数据,而在数据处理过程中该socket又有新的数据可读(EPOLLIN再次被触发),此时另一个线程被唤醒来读取这些新的数据,于是就出现了两个线程同时操作一个socket的局面。

        我们期望一个socket连接在任何时候都只被一个线程处理,可以采用EPOLLONESHOT事件实现。对于注册了EPOLLONESHOT事件的文件描述符,操作系统最多触发其上注册的一个可读、可写或者异常事件,且只触发一次,除非使用epoll_cnt函数重置改文件描述符上注册的EPOLLONESHOT事件。

SELECT、POLL和EPOLL区别:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

为什么iPhone支持整页中文OCR应用很少?有什么好的解决方法?

iPhone上面没有支持中文整页OCR识别的app&#xff0c;这是一个值得探讨的问题。OCR&#xff0c;即光学字符识别&#xff0c;是一种将纸质文档或图片中的文字转化为可编辑文本的技术。随着科技的发展&#xff0c;OCR技术已经广泛应用于各个领域&#xff0c;包括文档处理、图像识…

C/C++ 入门(7)string类(STL)

个人主页&#xff1a;仍有未知等待探索-CSDN博客 专题分栏&#xff1a;C 请多多指教&#xff01; 目录 一、标准库中的string 1、了解 2、string类常用接口说明 1、常见的构造函数 2、容量操作 ​编辑 3、访问及遍历操作 4、修改操作 5、非成员函数 二、string类实现 …

搭建知识库-DataWhale笔记

词向量及向量知识库介绍 词向量 词向量定义 在机器学习和自然语言处理&#xff08;NLP&#xff09;中&#xff0c;词向量&#xff08;Embeddings&#xff09;是一种将非结构化数据&#xff0c;如单词、句子或者整个文档&#xff0c;转化为实数向量的技术。这些实数向量可以被…

Unet网络架构讲解(从零到一,逐行编写并重点讲解数据维度变化)

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️感谢大家点赞&#x1f44d;&…

大珩PPT助手一键颜色设置

大珩PPT助手最新推出的一键设置文字颜色和背景色功能&#xff0c;为用户在创建演示文稿时带来了更便捷、高效的体验。这一功能使用户能够轻松调整演示文稿中文字的颜色和幻灯片的背景色&#xff0c;以满足不同场合和主题的需要。 以下是该功能的几个关键特点和优势&#xff1a…

恶意游戏并非传说:它们甚至在 Steam 上也存在

三月份&#xff0c;玩家们在在线平台上遇到了热门游戏的假克隆。在受害者中&#xff0c;有一位用户购买了一款假冒游戏《最后纪元》&#xff08;Last Epoch&#xff09;&#xff0c;但玩了几个小时后却出现了 "蓝屏死机"。 在联系了技术支持后&#xff0c;Steam 将钱…

代码随想录算法训练营Day6 | 242.有效的字母异位词 ●349. 两个数组的交集 ● 202. 快乐数● 1. 两数之和

基础&#xff1a; 1.哈希表是根据关键值进行直接访问的数据结构&#xff0c;时间复杂度是O(1)&#xff0c;也就是通过数组的索引下标&#xff0c;直接访问数组中的元素哈希表的作用就是用来快速判断一个元素是否出现在集合里。 2.常见的哈希结构&#xff1a; 数组set &#…

CB2-2CARD之Debian(Bookworm)安装Gnome看CCTV

CB2-2CARD之Debian&#xff08;Bookworm&#xff09;安装Gnome看CCTV 1. 源由2. 需求3. Debian系统桌面3.1 系统安装3.2 磁盘扩容3.3 系统更新3.4 Gnome安装 4. 测试4.1 CCTV网页测试4.2 系统空闲测试4.3 Firefox CPU占用率测试 5. 总结 1. 源由 近些年来&#xff0c;随着国内…

arm架构,django4.2.7适配达梦8数据库

【Python相关包版本信息】 Django 4.2.7 django-dmPython 3.1.7 dmPython 2.5.5 【达梦数据库版本】 DM Database Server 64 V8 DB Version: 0x7000c 适配过程中发现的问题如下&#xff1a; 错误一&#xff1a;d…

Git | 分支管理

Git | 分支管理 文章目录 Git | 分支管理1、理解分支2、创建分支&&切换分支3、合并分支4、删除分支5、合并冲突6、分支管理策略合并分支模式实际工作中分支策略bug分支删除临时分支 1、理解分支 分支就类似分身。 在版本回退中&#xff0c;每次提交Git都会将修改以git…

快速部署stable diffusion@Ubuntu

Stable Diffusion可以根据文本描述生成相关的图像&#xff0c;是当前最热门的文生图模型。 在Ubuntu下&#xff0c;可以选择快速安装&#xff0c;或者手动一步步安装。 快速安装 使用文档中的方法&#xff0c;先下载一个sh文件&#xff0c;然后执行这个文件&#xff0c;就自动…

ChatGPT助力测试领域!探索人工智能编写测试用例的新前景

简介 测试用例是测试人员的核心工作内容&#xff0c;是测试人员思想的“实现类”&#xff0c;其充分体现了测试的思路&#xff0c;可以为后续的测试行为提供指导&#xff0c;是测试人员了解业务的重要根据和质量之根本。如果测试用例设计得不完成&#xff0c;出现了遗漏&#x…

git merge 和 git rebese的区别

git merge 和 git rebese的区别 拉取分支和合并代码会涉及两种选择&#xff0c;git merge 和 git rebase&#xff1a; rebase&#xff1a;变基&#xff0c;会有一个干净的分支&#xff0c;但是对于记录来源不够清楚merge&#xff1a;合并&#xff0c;git 分支看起来比较混乱&…

java-Arrays

一、Arrays的概述 Arrays是操作数组的工具类 二、Arrays的常用方法 Arrays的常用方法基本上都被static静态修饰&#xff0c;因此在使用这些方法时&#xff0c;可以直接通过类名调用 1.toString 语法&#xff1a;Arrays.toString(数组) 用于将数组的元素转换为一个字符串&a…

(mac)Prometheus监控之Node_exporter(CPU、内存、磁盘、网络等)

完整步骤 1.启动 Prometheus 普罗米修斯 prometheus --config.file/usr/local/etc/prometheus.yml 浏览器访问 http://localhost:9090/targets 2.启动Node_exporter node_exporter 访问&#xff1a;http://localhost:9100 3.启动grafana brew services start grafana 访问…

Redis - Redisson tryLock 函数参数分析

这里有三个参数&#xff1a; waitTime&#xff1a;等待时间leaseTime&#xff1a;超时施放时间TimeUnit&#xff1a;时间单位 等待时间 如果 ABC… 多个线程去抢夺一把锁&#xff0c;A 成功了&#xff0c;如果设置的是 -1&#xff0c;那么 BCD... 就不等待&#xff0c;直接返…

计算机工作者学习平台

给大家分享了几个非常有用的学习平台&#xff0c;可以作为参考&#xff0c;具体为&#xff1a; 1.中国大学MOOC 中国大学MOOC_优质在线课程学习平台 2.牛客 牛客网 - 找工作神器|笔试题库|面试经验|实习招聘内推&#xff0c;求职就业一站解决_牛客网 3.CSDN https://www…

气压计LPS22HB开发(1)----轮询获取气压计数据

气压计LPS22HB开发----1.轮询获取气压计数据 概述硬件准备视频教学样品申请源码下载产品特性通信模式速率生成STM32CUBEMX串口配置IIC配置CS和SA0地址设置串口重定向参考程序SA0设置模块地址获取ID复位操作BDU设置设置低通滤波器设置速率轮询读取数据演示 概述 最近在弄ST的课…

[Algorithm][二分查找][山峰数组的峰顶索引][寻找峰值][寻找旋转排序数组中的最小值][0~n-1中缺失的数字]详细讲解

目录 1.山脉数组的峰顶索引1.题目链接2.算法原理详解3.代码实现 2.寻找峰值1.题目链接2.算法原理详解3.代码实现 3.寻找旋转排序数组中的最小值1.题目链接2.算法原理详解3.代码实现 4.0〜n-1 中缺失的数字1.题目链接2.算法原理详解3.代码实现 1.山脉数组的峰顶索引 1.题目链接…

TaskWeaver使用记录

TaskWeaver使用记录 1. 基本介绍2. 总体结构与流程3. 概念细节3.1 Project3.2 Session3.3 Memory3.4 Conversation3.5 Round3.6 Post3.7 Attachment3.8 Plugin3.9 Executor 4. 代码特点5. 使用过程5.1 api调用5.2 本地模型使用5.3 添加插件 6. 存在的问题与使用体验6.1 判别模型…