BIO、NIO、IO多路复用(select/poll/epoll)、信号驱动IO、异步IO

2023-05-16

BIO、NIO、IO多路复用【select/poll/epoll】、信号驱动IO、异步IO

    • Linux用户空间和内核空间
    • 阻塞IO(Blocking IO)
    • 非阻塞IO(Noblocking IO)
    • IO多路复用(IO Multilpexing)
      • select
      • poll
      • epoll
    • 信号驱动IO(Signal Driven IO)
    • 异步IO(Asynchronous IO)
    • 五种IO模型对比

Linux用户空间和内核空间

程序运行时,为了避免用户应用发生冲突,甚至发生系统冲突,用户应用和内核是分开的:
进程的寻址空间会划分为两部分:内核空间、用户空间。
当进程运行在用户空间时,我们称之为用户态,当进程运行在内核空间的时候,我们称之为内核态。

  • 用户空间只能执行受限的命令,而且不能调用系统资源,必须通过内核提供的接口来访问。
  • 内核空间可以执行特权命令,调用一切资源。

Linux为了提高IO效率会在用户空间和内核空间都加入缓冲区:

  • 写数据时,要把用户缓冲数据拷贝到内核缓冲区,然后写去设备。
  • 读数据时,要从设备读取数据到内核缓冲区,然后拷贝到用户缓冲区。

其中读数据的过程如下所示:
在这里插入图片描述
读数据主要分为两部分:

  1. 等待数据就绪:我们向内核空间发起请求,等待数据,内核空间将数据准备好之后,放到内核的缓冲区。
  2. 从内核缓冲区拷贝数据到用户缓冲区。然后用户程序就可以对用户缓冲区内的数据进行操作。

阻塞IO(Blocking IO)

阻塞IO指的是在 等待数据阶段由内核拷贝数据到用户空间阶段 都阻塞等待的IO。

等待数据阶段: 用户进程尝试读取数据,此时数据尚未到达,内核需要等待数据,此时用户线程也处于阻塞状态。
拷贝数据阶段: 数据到达并拷贝到内核缓冲区,代表已经就绪;将内核数据拷贝到用户缓冲区;拷贝过程中,用户进程依然阻塞等待;拷贝完成,用户进程阻塞解除,处理数据。
在这里插入图片描述
用户应用调用recvfrom读取数据,数据未准备好,用户应用阻塞等待,直到1,2都完成才能解除阻塞等待。

非阻塞IO(Noblocking IO)

非阻塞IOrecvfrom操作会立即返回结果而不是阻塞用户进程。

等待数据阶段:
用户进程尝试读取数据,此时数据尚未到达,内核需要等待数据,返回异常给用户进程,用户进程拿到error之后,再次读取失败,之后不停调用recvfrom,直到数据就绪。

数据拷贝阶段:
用户进程阻塞等待,直到拷贝完成,用户进程解除阻塞,处理数据。

在这里插入图片描述
虽然第二个阶段是非阻塞状态,但是性能并没有得到提高,而且忙等机制会导致CPU空转,CPU使用率暴增。

IO多路复用(IO Multilpexing)

文件描述符File DescriptorFD, 是一个从0开始的无符号整数,用来关联Linux中的一个文件。在Linux中一切皆文件,例如常规文件、视频、硬件设备等,也包括网络套接字。

IO多路复用是利用单个线程来同时监听多个FD,并在FD可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。

等待数据阶段:用户进程调用select,指定要监听的FD集合,内核监听FD对应的多个socket,任意一个或多个socket数据就绪就返回readable,该过程中用户线程阻塞。

数据拷贝阶段:用户线程找到就绪的socket,依次调用recvfrom读取数据,内核将数据拷贝到用户空间,用户进程处理数据。
在这里插入图片描述
IO多路复用中监听FD的方式,有多种实现,常见的有:selectpollepoll
其中:

  • selectpoll只会通知用户进程有FD就绪,但不确定是哪个FD,需要用户进程遍历FD来确认。
  • epoll则会在通知用户进程FD就绪的同时,把已经就绪的FD写入用户空间。

select

select的部分代码如下:

// 定义类型别名 __fd_mask,本质是 long int
typedef long int __fd_mask;
/* fd_set 记录要监听的fd集合,及其对应状态 */
typedef struct {
    // fds_bits是long类型数组,长度为 1024/32 = 32
    // 共1024个bit位,每个bit位代表一个fd,0代表未就绪,1代表就绪
    __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
    // ...
} fd_set;
// select函数,用于监听fd_set,也就是多个fd的集合
int select(
    int nfds, // 要监视的fd_set的最大fd + 1
    fd_set *readfds, // 要监听读事件的fd集合
    fd_set *writefds,// 要监听写事件的fd集合
    fd_set *exceptfds, // // 要监听异常事件的fd集合
    // 超时时间,null-用不超时;0-不阻塞等待;大于0-固定等待时间
    struct timeval *timeout
);

其中,存储FD的数据结构为fd_set, fd_set中有一个数组fds_bits,其大小为32,fds_bits数组的类型为long int4字节,因此总共为32x4x8=1024位,每个bit位代表一个fd0代表未就绪,1代表就绪

其过程如下:
在这里插入图片描述
在这里插入图片描述
select模式存在的问题

  • 需要将整个fd_set从用户空间拷贝到内核空间,select结束还要再次拷贝回用户空间
  • select无法得知具体是哪个fd就绪,需要遍历整个fd_set
  • fd_set监听的fd数量不能超过1024

poll

poll的部分代码如下:

// pollfd 中的事件类型
#define POLLIN     //可读事件
#define POLLOUT    //可写事件
#define POLLERR    //错误事件
#define POLLNVAL   //fd未打开

// pollfd结构
struct pollfd {
    int fd;     	  /* 要监听的fd  */
    short int events; /* 要监听的事件类型:读、写、异常 */
    short int revents;/* 实际发生的事件类型 */
};
// poll函数
int poll(
    struct pollfd *fds, // pollfd数组,可以自定义大小
    nfds_t nfds, // 数组元素个数
    int timeout // 超时时间
);

IO流程:

  1. 创建pollfd数组,向其中添加关注的fd信息,数组大小自定义
  2. 调用poll函数,将pollfd数组拷贝到内核空间,转链表存储,无上限
  3. 内核遍历fd,判断是否就绪
  4. 数据就绪或超时后,拷贝pollfd数组到用户空间,返回就绪fd数量n
  5. 用户进程判断n是否大于0
  6. 大于0则遍历pollfd数组,找到就绪的fd

select对比:

  • select模式中的fd_set大小固定为1024,而pollfd在内核中采用链表,理论上无上限
  • 虽然fd数量无限制,但是监听FD越多,每次遍历消耗时间也越久,性能反而会下降

epoll

epoll模式是对select和poll的改进,它提供了三个函数:

struct eventpoll {
    //...
    struct rb_root  rbr; // 一颗红黑树,记录要监听的FD
    struct list_head rdlist;// 一个链表,记录就绪的FD
    //...
};
// 1.创建一个epoll实例,内部是event poll,返回对应的句柄epfd
int epoll_create(int size);
// 2.将一个FD添加到epoll的红黑树中,并设置ep_poll_callback
// callback触发时,就把对应的FD加入到rdlist这个就绪列表中
int epoll_ctl(
    int epfd,  // epoll实例的句柄
    int op,    // 要执行的操作,包括:ADD、MOD、DEL
    int fd,    // 要监听的FD
    struct epoll_event *event // 要监听的事件类型:读、写、异常等
);
// 3.检查rdlist列表是否为空,不为空则返回就绪的FD的数量
int epoll_wait(
    int epfd,                   // epoll实例的句柄
    struct epoll_event *events, // 空event数组,用于接收就绪的FD
    int maxevents,              // events数组的最大长度
    int timeout   // 超时时间,-1用不超时;0不阻塞;大于0为阻塞时间
);

在这里插入图片描述
select模式存在的三个问题

  • 能监听的FD最大不超过1024
  • 每次select都需要把所有要监听的FD都拷贝到内核空间
  • 每次都要遍历所有FD来判断就绪状态

`poll模式的问题

  • poll利用链表解决了select中监听FD上限的问题,但依然要遍历所有FD,如果监听较多,性能会下降

epoll模式

  • 基于epoll实例中的红黑树保存要监听的FD,理论上无上限,而且增删改查效率都非常高
  • 每个FD只需要执行一次epoll_ctl添加到红黑树,以后每次epol_wait无需传递任何参数,无需重复拷贝FD到内核空间
  • 利用ep_poll_callback机制来监听FD状态,无需遍历所有FD,因此性能不会随监听的FD数量增多而下降

信号驱动IO(Signal Driven IO)

信号驱动IO是与内核建立SIGIO的信号关联并设置回调,当内核有FD就绪时,会发出SIGIO信号通知用户,期间用户应用可以执行其它业务,无需阻塞等待。

等待数据阶段

  1. 用户进程调用sigaction,注册信号处理函数
  2. 内核返回成功,开始监听FD
  3. 用户进程不阻塞等待,可以执行其它业务
  4. 当内核数据就绪后,回调用户进程的SIGIO处理函数

数据拷贝阶段

  1. 收到SIGIO回调信号
  2. 调用recvfrom,读取
  3. 内核将数据拷贝到用户空间
  4. 用户进程处理数据

在这里插入图片描述
当有大量IO操作时,信号较多,SIGIO处理函数不能及时处理可能导致信号队列溢出,而且内核空间与用户空间的频繁信号交互性能也较低。

异步IO(Asynchronous IO)

异步IO的整个过程都是非阻塞的,用户进程调用完异步API后就可以去做其它事情,内核等待数据就绪并拷贝到用户空间后才会递交信号,通知用户进程。

等待数据阶段

  1. 用户进程调用aio_read,创建信号回调函数
  2. 内核等待数据就绪
  3. 用户进程无需阻塞,可以做任何事情

数据拷贝阶段

  1. 内核数据就绪
  2. 内核数据拷贝到用户缓冲区
  3. 拷贝完成,内核递交信号触发aio_read中的回调函数
  4. 用户进程处理数据

异步IO模型中,用户进程在两个阶段都是非阻塞状态。

五种IO模型对比

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

BIO、NIO、IO多路复用(select/poll/epoll)、信号驱动IO、异步IO 的相关文章

  • cas 单点登陆实战-sso-config篇(五)

    本篇我们讲解cas单点登陆在与shiro集成 xff0c 在与redis集成遇到的问题 先看完整代码吧 package com madnet config import com google common base CaseFormat i
  • cas 登陆时验证信息无效

    1 先查看缓存中的地址等是否正确 xff0c 不正确清除缓存重新打包 2 检查证书是否正确
  • crontab任务堆积导致启动大量进程的处理(flock)

    crontab在执行任务过程中 xff0c 可能由于任务执行错误或者在下一个任务开始前 xff0c 前一个任务没有执行完成 xff0c 导致大量的进程产生 xff0c 最后导致死机等 可以使用flock来加锁防止启动大量进程 xff1a f
  • Ue4行为树学习笔记 二

    Ue4行为树学习笔记 二 前言引用资料 行为树创建于布局任务设立 追逐随机巡逻AI控制器的设置最终设置尾声 前言 因为篇幅原因 xff0c 没看过上篇还请移步至 Ue4行为树学习笔记 一 引用资料 行为树快速入门指南 UE4学习笔记 xff
  • Python实现人脸识别

    文章目录 前言一 face recognition的安装1 安装dlib1 安装face recognition 二 上源码总结 前言 face recognition face recognition中文使用说明 号称是世界上最简单的开源
  • GPU, CUDA,cuDNN三者的关系总结

    GPU CUDA cuDNN三者的关系总结 CPU 和 GPU cpu和gpu结构展示 GPU xff08 图像处理器 xff0c Graphics Processing Unit xff09 和CPU xff08 中央处理器 xff0c
  • android应用内多进程的实现

    Android应用内多进程的介绍 正常情况下 xff0c 一个apk启动后只会运行在一个进程中 xff0c 其进程名为AndroidManifest xml文件中指定的应用包名 xff0c 所有的基本组件都会在这个进程中运行 但是如果需要将
  • 只需两步获取任何小程序源码

    前言 xff1a 这个博文的转载挺多的 xff0c 不过原文在更新 xff0c qwerty472123大神的脚本也在更新 xff0c 好多文章转载后就不再更新了 xff0c 看以前的步骤操作 xff0c 可能会引起奇怪的bug 这种方法
  • linux环境变量、交叉编译工具链gcc

    linux环境变量 1 linux环境变量 Linux 是一个多用户操作系统 xff0c 每个用户都有自己专有的运行环境 用户所使用的环境由一系列变量所定义 xff0c 这些变量被称为环境变量 系统环境变量通常都是大写 每个用户都可以根据需
  • Ubuntu解决登录界面无限循环

    由于配置scala和spark xff0c 修改了 etc profile文件 xff0c 导致Ubuntu登录界面无线循环 解决方法 xff1a 将profile修改正确 这是我的正确的文件内容 xff0c 供参考 span class
  • 如何优化Win11右键菜单

    Win 11 微软已经在10月5日发布了Windows 11正式版 xff0c 很多朋友也已经升级了 不过对于Win11的一些新设计 xff0c 并不是所有人都能适应的 xff0c 例如新的右键快捷菜单 xff0c 就不少朋友表示接受不了
  • 简单消费者组件的抽象思考(C++11)

    异步输出日志 平时开发过程中总是不可避免会用到生产者 消费者模型来实现一些具体的功能 比如在应用程序中 xff0c 我们希望在关键的代码附近输出一些日志 到文件 xff0c 以备程序运行出现bug时尽可能地知道更多的运行时信息 xff0c
  • 利用Cloudflare API批量添加域名至Cloudflare账户

    在Cloudflare中其实是有提供API的 xff0c 可以利用其功能进行批量添加域名 xff0c 在这篇文章中 xff0c 我将尝试看看如何解决这个问题 第一 准备工作 1 拥有一个Cloudflare账户 2 需要执行脚本的Linux
  • 如何用vnc远程连接mac系统

    在mac os系统安装好后 xff0c 怎么用vnc来进行远程连接呢 xff0c 步骤在下方 xff1a 1 打开系统偏好设置 2 点击共享 3 勾选屏幕共享 选择允许访问的用户 点击电脑设置 4 填好vnc连接的密码 5 用vnc登录 x
  • 学生时代的书单

    大话系列的书 xff0c 用独特的行文风格 xff0c 以风趣 幽默的语言向读者讲述 概念原理知识 xff0c 用漫画式的插图帮助读者理解晦涩 枯燥的技术 xff0c 让我们在快乐中掌握知识 xff01 1 大话通信 通信基础知识读本 作者
  • NLTK语料库nltk.download()安装失败及下载很慢的解决方法

    一 解决nltk download 安装失败 import nltk nltk download 下载nltk语料库出现getaddrinfo failed 如下错误 xff1a 这里只需将Server Index路径改成NLTK官网htt
  • windows虚拟机ping不通解决方法

    防火墙在作祟 简单暴力方法一 xff1a 直接关了防火墙 安全操作方法二 xff1a 打开入站规则就OK 如果是端口不通 xff0c 新建个端口规则解决啦
  • Spring框架(一)---------基本配置

    一 spring框架的配置一般姿势 1 导包 2 创建对象 3 配置文件 xff08 src applicationContext xml xff09 3 1 导入约束 3 2 配置applicationContext xml文件 3 3测
  • windows下面notepad++编写的文件未保存,电脑死机,文件找回

    notepad恢复未保存的文件 xff0c 备份文件 C Users 你当前用户的用户名 AppData Roaming Notepad 43 43 backup可以恢复 如果找不到此文件 因为文件被隐藏了 xff0c 打开隐藏文件即可 抖
  • idea自带的Maven添加阿里镜像

    打开idea xff0c 并打开设置 在搜索框查找Maven xff0c 可以看到idea使用的Maven路径 xff0c 配置文件路径 xff0c 以及仓库路径 重点是看配置文件 xff1a settings xml 如果在配置文件路径下

随机推荐

  • Ubuntu16.04开机失败—进入tty1终端修复

    Ubuntu16 04开机失败 进入tty1终端修复 如图所示 xff0c 我的Ubuntu16 04开机的时候报错 xff0c 提示 etc profile 文件的第34行出错 我想起了这个是自己安装mysql和sqoop的时候配的路径
  • 下载网页中的视频的两种方法

    方法一 xff1a 使用360或者IE浏览器 1 进入播放视频的网页 xff0c 播放视频并缓冲完全 xff1b 2 点击浏览器 工具 栏菜单中 Internet 选项 xff1b 3 在弹出的窗口中间部位找到 设置 xff1b 4 在新窗
  • tigerVNC的简单使用教程(CentOS的远程桌面连接)

    tigerVNC的简单使用教程 xff08 CentOS的远程桌面连接 xff09 DayDreamingBoy的博客 CSDN博客 tigervnc 1 环境和软件准备 1 CentOS 6 3下 root 64 localhost rp
  • gazebo视角调整

    看见上图中的橘黄色的图标了吗 xff1f 点击下拉框 xff0c 就可以调整自己的视角 xff0c 然后配合Ctrl 43 鼠标拖拽就OK了 参考博客 参考一
  • RTX 线程通信之——内存池

    文章目录 Memory Pool为什么需要内存池 xff1f 什么是内存池 xff1f RTX内存池API 案例 xff1a 按键控制LED灯定义相关创建相关执行相关实验效果 小结参考资料 Memory Pool 内存池 Memory Po
  • springboot项目多环境配置及常见配置名的含义

    强烈推荐大家想学习springboot项目相关知识的 xff0c 可以看一下Gitee上大佬整理的Spring Boot基础教程 xff0c 非常适合初学者和进阶学习 xff1a 传送门 我们在进行项目开发时 xff0c 经常同一个应用需要
  • 聊聊linux中的文件种类、文件名、文件扩展名

    linux中的文件种类 文件名 文件扩展名详解 在使用 ls l指令后可以看到文件的类型 xff0c 其中第一个字符就是代表的文件的类型 xff0c 常见的文件类型是一般文件 和目录文件d 文件的类型 1 正规文件 xff08 regula
  • 华为2288 v5服务器安装centos7.9教程

    华为2288 v5服务器安装centos7 9教程 一 准备工作二 centos启动盘制作三 删除 配置RAID四 选择启动项1 开机按F112 选择u盘启动 五 centos安装1 选择语言2 选择安装方式3 选择安装位置 xff08 重
  • Pytorch-gpu版安装教程【注意:无需提前安装cuda和cudnn】

    Pytorch gpu版安装教程 注意 xff1a 无需提前安装cuda和cudnn 1 首先确保你已经安装好Anaconda2 查看自己电脑上显卡的信息 xff0c 通过显卡控制面板查看3 如何根据想要的cuda的版本下载相应的显卡驱动程
  • python调用有道翻译API进行翻译

    python调用有道翻译API进行翻译 步骤 python调用有道翻译API进行翻译准备调用API所需的APPID以及秘钥1 有道智云注册账号2 有道智云注册账号3 创建实例 xff0c 绑定应用4 查看官方文档 python实现对有道翻译
  • Linux安装cuda10.2

    Linux安装cuda10 2 安装其他版本的cuda也可以参考以下步骤 A 进入NVIDIA官网下载安装文件 百度搜索cuda 10 2 点开第一个网页 找到对应自己系统版本的安装文件命令 在命令行中执行给出的代码 xff0c 下载安装文
  • Windows 10 安装anaconda

    Windows 10 安装anaconda 1 下载anaconda安装包2 安装Anaconda3 配置Anaconda环境变量4 检验是否安装成功4 anaconda换源5 python换源 1 下载anaconda安装包 网页搜索清华
  • 排序算法-基数排序

    排序算法 基数排序 算法思想 基数排序是采用多关键字排序思想 xff08 即基于关键字各位的大小进行排序地 xff09 xff0c 借助 分配 收集 两种操作对单逻辑关键字进行排序 基数排序又分为最高位优先 MSD 降序 排序和最低位优先
  • No module named ‘cv2‘ 解决方法

    No module named cv2 解决方法 1 安装opencv python 在使用的虚拟环境中 xff0c 输入以下命令 pip span class token function install span opencv pyth
  • AttributeError:module ‘distutils‘ has no attribute ‘version

    AttributeError module distutils has no attribute 39 version 在使用torch utils tensorboard时 xff0c 出现错误 xff1a 出错语句from torch
  • CPU、GPU、NPU的区别

    CPU GPU NPU的区别 CPU CPU xff08 CentralProcessing Unit xff09 中央处理器 xff0c 是一块超大规模的集成电路 xff0c 主要逻辑架构包括控制单元Control xff0c 运算单元A
  • jdk(Linux+Windows)环境变量配置

    Windows jdk环境变量配置 xff1a PATH JAVA HOME bin JAVA HOME jre bin CLASSPATH JAVA HOME lib JAVA HOME lib tools jar JAVA HOME l
  • 并发编程-生产者消费者模式Java代码实现

    并发编程 生产者消费者模式Java代码实现 生产者消费者模式 生产者仅负责产生结果数据 xff0c 不关心数据该如何处理 xff0c 而消费者专心处理结果数据 消息队列是有容量限制的 xff0c 满时不会再加入数据 xff0c 空时不会再消
  • Java自定义线程池详解及代码实现(非直接调用ThreadPoolExecutor)

    JJava自定义线程池详解及代码实现 非直接调用ThreadPoolExecutor JDK中的线程池函数ThreadPoolExecutorJDK中的线程池执行任务时的流程自定义线程池业务分析自定义线程池的代码实现 注释详尽1 定义阻塞队
  • BIO、NIO、IO多路复用(select/poll/epoll)、信号驱动IO、异步IO

    BIO NIO IO多路复用 select poll epoll 信号驱动IO 异步IO Linux用户空间和内核空间阻塞IO xff08 Blocking IO xff09 非阻塞IO xff08 Noblocking IO xff09