Select、Poll、Epoll的使用和区别,多种IO的区别

2023-11-11

目录

一、四种IO分类

二、I/O多路复用select

三、I/O多路复用Poll

四、I/O多路复用Epoll

五、三种多路复用的区别总结

1、支持一个进程所能打开的最大连接数

2、fd剧增后带来的IO效率问题

3、 消息传递方式

4、索引就绪事件的效率上。


一、四种IO分类

  1. 阻塞IO:程序发起 I/O 操作时,如果数据还没有准备好,I/O 函数会一直阻塞当前线程,直到数据准备好为止
  2. 非阻塞IO:当程序发起 I/O 操作时,数据还没有准备好,立即返回给用户一个状态值,告诉程序数据还没有准备好,程序可以继续执行其他任务。(但需要用户自己循环调用I/O函数)
  3. 同步IO:(也称为阻塞 I/O)是指程序发起 I/O 操作后,必须等待 I/O 操作完成才能继续往下执行。在同步 I/O 中,I/O 函数会阻塞当前线程直到操作完成。
    • 优点:简单易用。
    • 缺点:会阻塞程序的执行,影响程序的性能。
    • 场景:在调用read/write时,必须等待函数返回(不关心返回值)之后才可以执行后面的代码。
  4. 异步IO:异步 I/O(也称为非阻塞 I/O)是指程序发起 I/O 操作后,不需要等待 I/O 操作完成就可以继续往下执行。在异步 I/O 中,I/O 函数会立即返回,操作完成I/O请求后会通过回调函数通知程序。
    1. 优点:可以提高程序的性能。
    2. 缺点:是实现比较复杂,需要使用回调函数等技术。

二、I/O多路复用select

使用步骤:

  1. 调用 select 函数:首先需要定义一个 fd_set 类型的集合,将要监听的文件描述符加入集合中,然后调用 select 函数,指定需要监听的文件描述符集合以及超时时间。

  2. 检查返回值:如果 select 函数返回正数n,表示有n个文件描述符就绪,可以进行读写操作。如果返回 0,表示超时。如果返回 -1,表示出现错误。

  3. 处理就绪文件描述符:通过遍历之前设置的文件描述符集合,检查其中的文件描述符是否就绪,如果就绪,则可以进行读写操作。

  4. 返回到步骤1:如果没有出现错误且没有超时,可以再次调用 select 函数继续监听文件描述符集合。

函数原型:

int select(int nfds,

          fd_set *readfds,

          fd_set *writefds,

          fd_set *exceptfds,

          struct timeval *timeout) 

其中:

  • nfds 参数指定需要监听的最大文件描述符值加一。
  • readfdswritefdsexceptfds 参数分别是指向文件描述符集合的指针,用于指定需要监听的文件描述符集合。
  • timeout 参数指定超时时间,可以是 NULL(表示永久阻塞),也可以是指向 timeval 结构体的指针。

在 select 函数返回之后,可以通过 FD_ISSET(fd, &fdset) 宏来判断某个文件描述符是否就绪,其中 fd 表示要判断的文件描述符,fdset 表示文件描述符集合。以下是与select搭配使用的宏。

FD_ZERO(fd_set* fds);
# 清空文件描述符集合 fds,也就是把所有比特位都置为 0

FD_SET(int fd, fd_set* fds);
# 把文件描述符 fd 加入集合 fds,也就是把对应的比特位置为 1

FD_ISSET(int fd, fd_set* fds); 
# 判断文件描述符 fd 是否在集合 fds 中,也就是判断对应的比特位是否为 1

FD_CLR(int fd, fd_set* fds);
# 把文件描述符 fd 从集合 fds 中删除,也就是把对应比特位置为 0

优点:

  • select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。

缺点和限制:

  • 文件描述符数量受限:select 函数可以监听的最大文件描述符数量是有限制的,一般默认是 1024。
  • 数据传输效率较低:对socket进行扫描时是线性扫描,即采用轮询的方法,而且当需要监听的文件描述符比较多时,频繁调用 select 函数会影响程序的效率,因为每次调用都需要进行一次遍历。
  • select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理,因此需要维护一个用来存放大量fd的数据结构(fd_set)。fd_set简单地理解为一个长度是1024的比特位,每个比特位表示一个需要处理的FD,如果是1,那么表示这个FD有需要处理的I/O事件,否则没有,其是连续存储的。每次select查询都要遍历整个事件列表。这样会使得用户空间和内核空间在传递该结构时复制开销大。
  • 每次select之后,事件集合都会被内核修改,再次调用select之前需要用户自己重新设置事件集合。

三、I/O多路复用Poll

使用步骤:

  1. 定义一个事件集合(实际上是数组,其中每个元素都是 struct pollfd结构体)。
  2. 事件绑定:把关注的文件描述符和在这个文件描述符上关注的事件绑定到一个 struct pollfd结构体中。
  3. 检查返回值:调用poll函数遍历事件集合,检查返回值返回正数n,表示有n个文件描述符就绪,可以进行读写操作。如果返回 0,表示超时。如果返回 -1,表示出现错误。
  4. 处理就绪的文件描述符:在已经就绪的事件中,根据事件和文件描述符的种类进行处理。
  5. 重复调用3~5。

函数原型:

#include <poll.h>

int poll(struct pollfd *fds,  /* 把文件描述符和事件绑定到一起 */
         nfds_t nfds,         /* pollfd的数量 */
         int timeout);        /* 超时时间 */

// poll事件结构体
struct pollfd {
               int   fd;         /* file descriptor */
               short events;     /* 需要监听的事件,由用户决定 */
               short revents;    /* 已经就绪的事件,由内核决定 */
           };

优点:

  • 把文件描述符和事件绑定到一起,使接口更简单。
  • 它没有最大连接数的限制,原因是它是基于链表来存储的。

缺点和限制:

  • poll本质上和select没有区别,每次调用时,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态。

四、I/O多路复用Epoll

步骤:

  1. 创建一个 epoll 实例,通过 epoll_create()epoll_create1() 系统调用实现。

  2. 向 epoll 实例中添加需要监听的文件描述符,可以通过 epoll_ctl() 系统调用实现,指定添加操作为 EPOLL_CTL_ADD

    int epoll_ctl(int epfd, /*epoll*/
                  int op,
                  int fd,
                  struct epoll_event *event);
    EPOLL_CTL_ADD  //往事件表上注册fd的事件
    EPOLL_CTL_MOD  //修改fd上的注册事件
    EPOLL_CTL_DEL  //删除fd上的注册事件
    

  3. 调用 epoll_wait() 等待文件描述符上的事件发生,如果有事件发生,则 epoll_wait() 函数会返回一个事件数组。

  4. 遍历事件数组,处理每个事件,根据事件类型进行相应的操作。

常见事件类型:

  • EPOLLIN:文件描述符可读;
  • EPOLLOUT:文件描述符可写;
  • EPOLLET:边缘触发模式;
  • EPOLLERR:文件描述符发生错误;
  • EPOLLRDHUP:对端关闭连接,或者半关闭连接;
  • EPOLLHUP:文件描述符被挂起;
  • EPOLLONESHOT:表示该事件只会被触发一次(同时只能被一个线程所处理)。

优点:

  • 在获取已就绪事件的时候,不会将整个文件描述符集全部遍历,只需要把那些被系统API异步唤醒后放入Ready队列的文件描述符返回给用户就好。
  • 独有ET边缘触发模式,可以提高I/O复用效率。
  • 不同于前两种哪种I/O机制,无法避免fd在操作过程中拷贝的问题。而epoll 使用了 mmap内存映射 (是指文件/对象的内存映射,被映射到多个内存页上),可以避免这个问题。

缺点:

  • 在编写轻量型的服务器时,和select相比,提升效果不大。

五、三种多路复用的区别总结

1、支持一个进程所能打开的最大连接数

  • select:单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是3232,同理64位机器上FD_SETSIZE为3264),当然我们可以对进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试。
  • poll:poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的。
  • epoll:虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接。

2、fd剧增后带来的IO效率问题

  • select:因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。
  • poll:同上
  • epoll:因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。

3、 消息传递方式

  • select:每次调用时,事件集合都需要在用户空间与内核空间之间传递,需要内核拷贝。
  • poll:同上
  • epoll:epoll通过内核和用户空间mmap共享内存来实现的,可以避免内核拷贝。

4、索引就绪事件的效率上。

  • select:O(N),每次调用,内核都需要遍历事件集合;且调用后,用户并不知道哪个事件就绪,还需要使用FD_ISSET来判断。
  • poll:O(N),同上,需要判断每个struct pollfd的revents是否就绪。
  • epoll:O(1),调用epoll_wait时,内核直接把ready队列里的事件集合返回给用户,且这些事件都是已就绪的。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Select、Poll、Epoll的使用和区别,多种IO的区别 的相关文章

随机推荐

  • IC617

    tip 个人日常笔记 解决的问题 导入一个由很多MOS晶体管组成的cdl网表 网表部分内容如下图所示 SUBCKT 512x2ESDA8 指的是 接下来将对512x2ESDA8这一模块进行定义 紧跟在后面的 A 8 A 7 是对这一模块管脚
  • 【Java多数据源实现教程】实现动态数据源、多数据源切换方式

    前言 本文为 Java多数据源实现教程 相关知识 由于自己最近在做导师的项目的时候需要使用这种技术 于是自学了相关技术原理与实现 并将其整理如下 具体包含 多数据源的典型使用场景 包含业务复杂场景 读写分离场景 多数据源实现原理及实现方法
  • JavaScript教程,零基础到入门,switch语句,case分组,类型,函数声明,局部变量,外部变量,参数,返回值,函数命名,

    switch 语句 switch 语句可以替代多个 if 判断 switch 语句为多分支选择的情况提供了一个更具描述性的方式 语法 switch 语句有至少一个 case 代码块和一个可选的 default 代码块 就像这样 switch
  • go 使用方式

    一 GO基本概念 Go是一个开源的编程语言 它能让构造简单 可靠且高效的软件变得容易 Go是从2007年末由Robert Griesemer 罗伯特 格利茨默 Rob Pike 罗伯 派克 gt Ken Thompson 肯 汤普森 主持开
  • C++预处理器

    预处理器是一些指令 指示编译器在实际编译之前所需完成的预处理 所有的预处理器指令都是以井号 开头 只有空格字符可以出现在预处理指令之前 不会以分号 结尾 define 预处理 define 预处理指令用于创建符号常量 该符号常量通常称为宏
  • Qt之程序发布以及打包成exe安装包

    1 简述 Qt 项目开发完成之后 需要打包发布程序 而因为用户电脑上没有 Qt 配置环境 所以需要将 release 生成的 exe 文件和所依赖的 dll 文件复制到一个文件夹中 然后再用 Inno Setup 打包工具打包成一个 exe
  • ArcGIS环境搭建及地图服务发布

    ArcGIS的环境需要如下文件 SQL Server2012 ArcGIS for Desktop ArcGIS for Server 目前网上提供的ArcGIS下载都是10 2 0版本 这个版本与win7的某几个补丁有冲突 会导致Publ
  • STM32配合火焰传感器的火灾报警

    实验材料 STM32F03 我这里用的是正点原子的战舰 火焰传感器 还有个蜂鸣器 我这个开发板自带 也是可外接的 火焰传感器介绍 工作原理 传感器模块在环境火焰光谱或者光源达不到设定阈值时 DO 口输出低电平 当外界环境火焰光谱或者光源超过
  • elasticsearch 编写java程序报错Exception in thread "main" java.lang.NoClassDefFoundError: org/elasticsearch

    java程序启动报错 Exception in thread main java lang NoClassDefFoundError org elasticsearch plugins NetworkPlugin ERROR StatusL
  • 微信小程序开发--2.6onLoad() 和onShow()的区别

    1 onLoad 页面第一次加载时触发 从跳转页面返回时不能触发 可以传递参数 代码示例 onLoad function options console log options console log options id var id o
  • SQL注入---联合注入

    Union联合注入攻击 1 联合注入的思路 会显示输出内容时 才考虑使用Union注入 可以在输入框中或者 URL 中输入内容 如果不能在输入框内输入内容 则需要使用 Burp suite 使用 重发器 修改 id 中的内容进行爆破数据 l
  • C++中实现Stack

    栈的实现 栈 示例代码 开发环境 运行结果 栈 栈本着先进后出的原则 来存取数据 作为数据结构中的一种 这里不多介绍相关栈 仅以此文记录C 中栈的实现 可帮助提升编程能力与对栈的理解 示例代码 直接上代码 SeqStack h pragma
  • Windows密码破解

    这里主要介绍两种方法来破解Windows的开机密码 一 利用五次shift漏洞来对win7 win10进行破解 此方法只适用于win7或者早期的win10 此方法主要是利用Windows开机时默认按五次shift键会启用粘贴键程序如下图 我
  • 手动推导LogisticRegression建模结果

    usr bin env python3 coding UTF 8 Date 2023 8 25 15 51 Author HELIN import numpy as np from sklearn model selection impor
  • 如何从Windows切换到Linux

    作者 栈栈 链接 CU技术社区 微软已经马上准备在2020年1月份终止对Windows 7的支持 这意味着您将不再获得bug修复或安全更新 如果您是Windows 7的最终支持者之一 并且不想陷入一个不安全的系统 则可以选择 升级到Wind
  • Verilog功能模块——Uart收发

    摘要 本文分享了一种通用的Uart收发模块 可实现Uart协议所支持的任意波特率 任意位宽数据 5 8 任意校验位 无校验 奇校验 偶校验 1校验 0校验 任意停止位 1 1 5 2 的数据传输 此模块需要搭配FIFO使用 以消除发送端和接
  • 最新AI创作系统ChatGPT网站源码+详细图文搭建教程/支持GPT-4/支持AI绘画/Prompt应用/访客体验功能

    一 SparkAI创作系统 如何搭建部署AI创作ChatGPT系统呢 小编这里写一个详细图文教程吧 SparkAi使用Nestjs和Vue3框架技术 持续集成AI能力到AIGC系统 1 1 程序核心功能 程序已支持ChatGPT3 5 4
  • Python:安装Flask web框架hello world示例

    安装easy install pip install distribute 安装pip easy install pip 安装 virtualenv pip install virtualenv 激活Flask pip install Fl
  • 搭建私人图床结合内网穿透实现公网访问,让您的摄影作品连接世界

    文章目录 1 树洞外链网站搭建 1 1 下载安装树洞外链 1 2 树洞外链网页测试 1 3 cpolar的安装和注册 2 本地网页发布 2 1 Cpolar临时数据隧道 2 2 Cpolar稳定隧道 云端设置 2 3 Cpolar稳定隧道
  • Select、Poll、Epoll的使用和区别,多种IO的区别

    目录 一 四种IO分类 二 I O多路复用select 三 I O多路复用Poll 四 I O多路复用Epoll 五 三种多路复用的区别总结 1 支持一个进程所能打开的最大连接数 2 fd剧增后带来的IO效率问题 3 消息传递方式 4 索引