【IO模型】select、poll、epoll的区别

news2024/12/27 11:38:36

文章目录

  • 五种IO模型
    • 阻塞IO
    • 非阻塞IO
    • 信号驱动IO
    • IO复用
    • 异步IO
  • IO复用的原理
    • select
      • select原理及缺点
    • poll
      • poll的原理及其缺点
    • epoll
      • epoll_create
      • epoll_ctl
      • epoll_wait
      • epoll的原理
      • 水平触发和边缘触发
      • epoll的优点

五种IO模型

I/O模型是操作系统中用于管理输入输出操作的机制。不同的I/O模型在处理I/O操作时,资源利用效率和响应速度各有不同。主要的I/O模型包括阻塞I/O非阻塞I/OI/O复用信号驱动I/O异步I/O

阻塞IO

在阻塞I/O模型中,系统调用(如read或recv)在等待I/O操作完成时,整个进程会被阻塞,直到数据被成功读入或写入。
阻塞状态下,进程不会做任何其他工作,CPU资源被挂起

特点:

  • 简单易用,代码编写和理解都较为直观
  • 效率低下,因为进程在等待I/O操作完成时无法执行其他任务,资源利用率不高。

非阻塞IO

非阻塞I/O模型使系统调用立即返回,而不管I/O操作是否完成。如果数据还没有准备好,系统调用会返回一个错误,进程可以在稍后再次尝试该操作。非阻塞I/O允许进程在等待I/O完成的同时执行其他任务,提高了CPU的利用率。
特点:

  • 提高了进程的并发性资源利用率,因为进程在等待I/O时可以执行其他操作。
  • 需要反复轮询检查 I/O状态,这种忙轮询(busy-waiting)可能导致CPU资源的浪费,特别是在I/O操作频繁或数据未就绪时。

信号驱动IO

在信号驱动I/O模型中,进程可以请求操作系统在I/O就绪时发出信号进程不必一直等待I/O操作的完成,而是继续执行其他任务,当I/O就绪时,操作系统通过发送信号通知进程进行处理
这减少了进程对I/O操作的等待时间,但需要处理信号。

特点:

  • 避免了忙轮询,提高了CPU利用率,同时进程可以处理其他任务。
  • 信号处理比较复杂,信号处理程序(Signal Handler)的设计和调试也相对困难。

IO复用

IO复用又叫多路复用模型,通过使用select、poll、epoll等系统调用,允许一个进程同时监控多个描述符(文件描述符)。一旦某个IO描述符就绪,进程就可以对其进行IO操作。

在这里插入图片描述
特点:

  • 允许单个进程同时处理多个I/O操作,资源开销较低,适合处理大量并发连接的场景
  • 对每次I/O操作都需要遍历所有的I/O描述符,尤其是在使用select或poll时,当描述符数量较多时,性能可能下降。

异步IO

异步I/O模型允许进程发起I/O操作后立即返回,而I/O操作在后台完成。操作系统会在I/O操作完成后,通知进程(通过回调函数、信号或状态变化)。
与其他模型不同的是,异步I/O模型中,I/O操作的完成不再由进程主动检查,而是由操作系统来管理和通知

特点:

  • 最高效的IO模型,进程完全不受IO操作的影响,可以处理其他业务,这也是为什么是异步的
  • 比较复杂,涉及异步回调或事件处理机制的设计,调试和维护较为困难

应用场景:高性能应用程序,比如高并发服务器,数据库系统等。

下面详细讲IO复用模型

IO复用的原理

select

函数原型

#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是一个指向timeval结构的指针,用于指向select调用的超时时间,其结构如下:
struct timeval {
    long tv_sec;  /* 秒 */
    long tv_usec; /* 微秒 */
};
  • 设置超时时间,可以将 timeout 设置为 NULL 以无限期等待,或者将 timeout 的 tv_sec 和 tv_usec 设置为 0 以立即返回(不等待)。

  • select函数返回值为就绪的文件描述符的数量,0表示超时,-1表示出错。

使用样例:

#include <sys/select.h>
#include <unistd.h> // for close
#include <stdio.h>
#include <sys/time.h> // for timeval
#include <string.h> // for memset

int main() {
    fd_set readfds;
    struct timeval tv;
    int retval;
    int fd = 0; // 通常是标准输入(stdin)

    FD_ZERO(&readfds); // 初始化文件描述符集合
    FD_SET(fd, &readfds); // 将文件描述符加入集合

    // 设置超时时间
    tv.tv_sec = 5; // 5秒超时
    tv.tv_usec = 0; // 0微秒

    retval = select(fd + 1, &readfds, NULL, NULL, &tv);
    if (retval == -1) {
        perror("select()");
    } else if (retval) {
        printf("Data is available now.\n");
        // 使用 FD_ISSET 检查 fd 是否在集合中
        if (FD_ISSET(fd, &readfds)) {
            char buffer[1024];
            int n = read(fd, buffer, sizeof(buffer));
            buffer[n] = '\0';
            printf("Read: %s", buffer);
        }
    } else {
        printf("No data within five seconds.\n");
    }

    return 0;
}

select原理及缺点

select函数通过将所有的文件描述符集合传递给内核,然后内核检查这些描述符的状态,如果有描述符就绪,select返回。

select的主要问题在于它的调用效率较低,因为每次调用都需要遍历所有的文件描述符集合,并且select本身有文件描述符数量的限制(通常为1024或2048)

poll

函数原型

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  • struct pollfd结构体定义如下:
struct pollfd {
    int fd;         /* 文件描述符 */
    short events;   /* 需要监视的事件 */
    short revents;  /* 实际发生的事件 */
};
  • events是需要监听的事件的掩码,可以是以下值的组合:

    • POLLIN:有数据可读。
    • POLLOUT:可以写入数据。
  • revents 是由 poll 填充的实际发生的事件。poll 返回时,你可以检查 revents 以确定哪些事件发生。

  • fds 是一个指向 pollfd 结构体数组的指针。每个 pollfd 结构体描述一个文件描述符和操作系统需要关心该文件描述符的事件,以及以及就绪的事件。

  • nfds 是 fds 数组的大小,即数组中包含的 pollfd 结构体的数量

  • timeout 以毫秒为单位指定等待事件发生的时间:

    • 如果 timeout 为负值,poll 将一直阻塞,直到有一个文件描述符的事件就绪。
    • 如果 timeout 为 0,poll 将立即返回,不等待任何事件。
  • 返回就绪的文件描述符的数量,如果超时返回0,出错返回-1。

使用样例:

#include <poll.h>
#include <unistd.h> // for close
#include <stdio.h>
#include <string.h> // for memset

int main() {
    struct pollfd fds[1];
    int timeout_msecs = 5000; // 5秒超时
    int ret;

    fds[0].fd = 0; // 通常是标准输入(stdin)
    fds[0].events = POLLIN; // 关注可读事件

    ret = poll(fds, 1, timeout_msecs);

    if (ret == -1) {
        perror("poll()");
    } else if (ret == 0) {
        printf("No data within five seconds.\n");
    } else {
        if (fds[0].revents & POLLIN) {
            printf("Data is available now.\n");
            char buffer[1024];
            int n = read(0, buffer, sizeof(buffer));
            buffer[n] = '\0';
            printf("Read: %s", buffer);
        }
    }

    return 0;
}

poll的原理及其缺点

poll与select类似,但它使用一个pollfd结构体数组来管理文件描述符和事件,避免了select中的文件描述符数量限制问题。poll也会遍历整个文件描述符数组,因此在描述符数量多时性能不佳。

epoll

epoll是Linux特有的I/O多路复用接口,分为三个主要操作函数:epoll_createepoll_ctlepoll_wait

epoll_create

创建一个epoll实例

int epoll_create(int size);
  • size:指定监听的文件描述符的最大数量
  • 返回一个epoll实例的文件描述符

epoll_ctl

控制epoll实例,注册、修改、删除监听的文件描述符

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  • epfd:由epoll_create返回的epoll实例的文件描述符
  • op:操作类型,可以是
    • EPOLL_CTL_ADD(添加)
    • EPOLL_CTL_MOD(修改)
    • EPOLL_CTL_DEL(删除)
  • fd:需要监控的文件描述符
  • event 是一个指向 epoll_event 结构体的指针,用于描述你对 fd 的监视需求。其结构定义如下:
struct epoll_event {
    uint32_t events; /* epoll 事件 */
    epoll_data_t data; /* 用户数据 */
};
  • events 是一个事件掩码,通常是以下值的组合:
    • EPOLLIN:有数据可读
    • EPOLLOUT:可以写入数据
    • EPOLLET:启用边缘触发
  • data 是一个联合体(union),允许你存储与文件描述符相关的用户数据。通常存文件描述符。

epoll_wait

获取就绪事件

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
  • pfd 是由 epoll_create 返回的 epoll 实例的文件描述符
  • events 是一个指向 epoll_event 结构体数组的指针,用于存储发生的事件(输出型参数)。
  • maxevents 是你准备处理的最大事件数,也就是 events 数组的大小
  • timeout 以毫秒为单位指定等待事件发生的时间:负数表示一直阻塞,直到有事件就绪。0表示无论是否有事件发生都会立刻返回。

epoll的原理

在这里插入图片描述

epoll的底层会创建一颗红黑树,是epoll_create函数的结果。epoll_create返回的文件描述符中对应的struct file结构体中有一个指针指向eventpoll 结构体,这个结构体指向一颗红黑树以及一个双链表

更具体的,每当使用 epoll_ctl 添加或修改一个文件描述符时,epoll 内部会将该文件描述符的各种信息创建一个结构体epitem对象里表示一个事件,插入到一个红黑树中。也就是说, epoll_ctl 函数的参数op实际上指的是红黑树节点的添加删除和修改红黑树的节点就是epitem对象。

这种设计使得 epoll 在处理大量文件描述符时,即使文件描述符数量非常大,增删改操作的效率仍然保持较高(红黑树的特性)

  • 每一个 epoll 对象都有一个独立的 eventpoll 结构体,用于存放通过 epoll_ctl 方法向 epoll 对象中添加进来的事件
  • 这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是 lgn,其中 n 为树的高度).
  • 所有添加到 epoll 中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,一个文件描述符的状态发生变化(如可读、可写等),内核会触发一个回调函数,将该文件描述符标记为就绪并将其加入到就绪队列中
  • 这个就绪队列本质就是一个双链表rdllist
  • 所以,调用epoll_wait函数其实就是在这个就绪队列里面获取节点,如果rdllist不为空就说明有关心的事件就绪了,同时就将事件拷贝到参数指针struct epoll_event *events中带给用户。

水平触发和边缘触发

水平触发又叫LT工作模式,边缘触发又叫ET模式。
这两种工作模式在处理就绪的文件描述符就绪事件的行为不同。

  • 水平触发:只要文件描述符的状态满足条件(如可读、可写),epoll_wait 就会一直返回该文件描述符的事件。这意味着如果文件描述符可读,epoll_wait 会持续通知应用程序,直到应用程序读取了所有可用的数据为止。epoll的默认工作模式就是水平触发。
  • 边缘触发:只有在文件描述符的状态从未就绪变为就绪时,epoll_wait 才会通知应用程序。这意味着如果应用程序没有处理完事件,而文件描述符状态没有变化,epoll_wait 不会再次返回该事件。所以,该模式下处理就绪事件一次就必须全部处理完,否则就会丢失事件

总结:

  • select和poll只支持LT工作模式,而epoll俩种都支持
  • 边缘触发更高效,避免了重复处理事件,但需要更谨慎的设计和实现
  • 水平触发可能会产生不必要的重复处理,通知效率低

epoll的优点

  • 接口使用方便,三个函数使用简单
  • 关心的文件描述符没有数量限制
  • 通过事件回调、红黑树、双链表,避免了遍历获得就绪事件提高了效率·。

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

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

相关文章

多线程 02:线程实现,创建线程的三种方式,通过多线程下载图片案例分析异同(Thread,Runnable,Callable)

一、概述 记录时间 [2024-08-08] 前置知识&#xff1a;Java 基础篇&#xff1b;Java 面向对象 多线程 01&#xff1a;Java 多线程学习导航&#xff0c;线程简介&#xff0c;线程相关概念的整理 Java 多线程学习主要模块包括&#xff1a;线程简介&#xff1b;线程实现&#xff…

MySQL 体系架构

文章目录 一. MySQL 分支与变种1. Drizzle2. MariaDB3. Percona Server 二. MySQL的替代1. Postgre SQL2. SQLite 三. MySQL 体系架构1.连接层2 Server层&#xff08;SQL处理层&#xff09;3. 存储引擎层1&#xff09;MySQL官方存储引擎概要2&#xff09;第三方引擎3&#xff0…

【java】一维数组

目录 一维数组内存分析Java虚拟机的内存划分一维数组内存解析 一维数组知识点一维数组课后练习 一维数组内存分析 Java虚拟机的内存划分 为了提高运行效率&#xff0c;就对空间进行了不同区域的划分&#xff0c;因为每一片区域都有特定的处理数据方式和内存管理方式。 java中…

cs224w colab0笔记

1.colab0 1.1 数据集 from torch_geometric.datasets import KarateClubdataset KarateClub() print(fDataset:{dataset}:) print() print(fNumber of graphs:{len(dataset)}) print(fNumber of features:{dataset.num_features}) print(fNumber of classes:{dataset.num_cl…

机器学习面试-核心概念-问题理解

1.机器学习的思想 计算机程序随着经验的积累&#xff0c;能够实现性能的提高。对于某一类任务T及其性能度量P&#xff0c;若一个计算机程序在T上以P衡量的性能随着经验E而自我完善&#xff0c;那么就称这个计算机程序在从经验E学习。 机器学习是人工智能的一个分支&#xff0c…

VMware Workstation 与 Device/Credential Guard 的不兼容问题

&#xff08;1&#xff09;出现问题 &#xff08;2&#xff09;出现问题原因&#xff1a; 我电脑原先弄过Hyper-V&#xff0c;这玩意是微软公司开发的一款虚拟化产品&#xff0c;它是微软第一个采用类似 VMware 和 Citrix Xen 等产品中的基于 hypervisor&#xff08;虚拟机监控…

基于区块链的供应链应用开发

区块链的供应链溯源应用开发 一 、环境准备 (1)更新镜像源 apt update(2)安装(openssl、jdk、git) apt -y install openssl default-jdk git(3)配置JAVA_HOME环境变量 echo “export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64/” >> /etc/profilesource /etc…

一键编译并启动一个 ARM Linux qemu 虚拟机

需要事先自己编译 qemu-system-arm 可执行文件&#xff1b; 1&#xff0c;编译创建ARM 虚拟机 1.1 一起从头开始版 cd 进一个空文件夹&#xff0c;然后 $ vim buildup.sh $ bash buildup.sh 访问github网络没什么问题&#xff1a; 硬编码了一个路径/home/hipper/ex_vexpre…

Java对象内存布局和Synchronized锁升级(二)

目录 对象内存布局对象头实例数据对齐填充锁在内存布局中的标志位 锁升级无锁偏向锁偏向锁升级 轻量级锁重量级锁 锁消除和锁粗化锁消除锁粗化 锁升级总结 对象内存布局 在HotSpot虚拟机里&#xff0c;对象在堆内存中的存储布局可以划分为三个部分:对象头(Header)、实例数据(I…

同态加密和SEAL库的介绍(八)性能

本篇会对比三种加密方案&#xff0c;同时每种方案配置三种参数。即九种情况下的各个操作的性能差异&#xff0c;为大家选择合适的方案和合适的参数提供参考。表格中所有时长的单位均为微妙&#xff0c;即 。 当然数据量比较大&#xff0c;为了方便大家查找&#xff0c…

应用商店故障(UOS系统)

应用商店故障&#xff08;UOS系统&#xff09; 1. 安装应用商店内的应用无法下载&#xff0c;更新系统时提示依赖关系被破坏&#xff0c;怎么办&#xff1f; 问题描述 安装应用商店内的应用无法下载&#xff0c;更新系统时均提示依赖关系被破坏 解决方案 1、可先建议用户尝试修…

day 22线程间通信

一、互斥锁 1、资源&#xff1a; 资源是有限的&#xff0c;在程序运行过程中&#xff0c;一段代码、一段空间、一个变量、CPU、内存都可以看做资源 2、互斥锁&#xff1a; 是一种资源,当一个线程任务加锁,其余线程任务无法再次加锁,直到解锁后才能加锁&#xff0c;互斥…

数据机房防静电措施有哪些?安装防静电地板时记住这几点

生活中静电无处不在&#xff0c;一般情况静电不会对我们有什么影响&#xff0c;但在一些特殊场合&#xff0c;比如数据机房、配电室、消控室、电子厂房等&#xff0c;静电的危害必须要引起重视&#xff0c;因为这些场合通常有比较多的电子设备&#xff0c;电子设备中有比较多的…

priority_queue模拟实现【C++】

文章目录 全部的实现代码放在了文章末尾什么是适配器模式&#xff1f;准备工作包含头文件定义命名空间类的成员变量什么是仿函数&#xff1f;比较仿函数在priority_queue中的作用通过传入不同的仿函数可以做到大堆和小堆之间的切换通过传入不同的仿函数可以做到改变priority_qu…

[Leetcode][Medium]-面试题 17.14.最小k个数-TOP K问题-快排/大根堆

一、题目描述 原题地址 二、整体思路 (1)、快排 数组中最小的k个数就是说把数组升序排列&#xff0c;求[0,k-1]区间上的数。 快排可以得到一个元素在升序排序的数组中的正确位置。在这个位置的左边区间[l,l2-1]上的元素都比它小&#xff0c;在这个位置的右边区间[r2,r]上的元素…

zabbix 监控软件

zabbix 监控软件 自带图形化界面&#xff0c;通过网页就可以监控所有的服务器的状态。 事件告警&#xff0c;邮箱通知&#xff08;噩梦&#xff09;。 zabbix是什么&#xff1f; web界面提供的分布式监控以及网络监控功能的开源的企业级的软件解决方案。 服务端 监控端 客…

App安装来源追踪的四大方案解析

App的开发者和运营商&#xff0c;都会研究分析渠道的效果&#xff0c;而对渠道来源的追根溯源是一切分析的基础。假如没有明确的安装来源数据&#xff0c;至少会造成以下几种后果&#xff1a; 没有安装来源数据&#xff0c;我们无法判断各个投放渠道流量的价值&#xff0c;也就…

全网首发!鸿蒙OS登上PC,冒风险流出内测,系统界面截图,过会儿就删

开玩笑的啦&#xff0c;其实这是Deepin操作系统的截图&#xff0c;很漂亮吧&#xff0c;这是deepin v23 rc2 的主题之一 鸿蒙还没有发&#xff0c;不知道24年末还能不能上&#xff0c;emmm

Python实战:基础语法

一、求解列表中的最大元素 import random#定义函数 def get_max(lst):x lst[0] #x存储的是元素的最大值#遍历操作for i in range(1,len(lst)):if lst[i] > x:x lst[i] #对最大值进行重新赋值return x#调用函数 lst [random.randint(1,100) for item in range(10)] print…

基于SiliconCloud快速体验GraphRag.Net

SiliconCloud介绍 SiliconCloud 基于优秀的开源基础模型&#xff0c;提供高性价比的 GenAI 服务。 不同于多数大模型云服务平台只提供自家大模型 API&#xff0c;SiliconCloud上架了包括 Qwen、DeepSeek、GLM、Yi、Mistral、LLaMA 3、SDXL、InstantID 在内的多种开源大语言模…