剖析IO原理和零拷贝机制

ops/2025/2/24 11:41:41/

目录

  • 1 Linux的五种IO模型
    • 1.1 模型调用的函数
      • 1.1.1 recv函数
      • 1.1.2 select函数
      • 1.1.3 poll函数
      • 1.1.4 epoll函数
      • 1.1.5 sigaction函数
    • 1.2 IO模型
      • 1.2.1 阻塞IO模型
      • 1.2.2 非阻塞IO模型
      • 1.2.3 IO复用模型
      • 1.2.4 信号驱动IO模型
      • 1.2.5 异步IO模型
      • 1.2.6 IO模型比较
  • 2 Java的BIO、NIO、AIO
    • 2.1 BIO(Blocking IO,同步阻塞式IO模型)
    • 2.2 NIO(Non-blocking IO,同步非阻塞式IO模型)
    • 2.3 AIO(Asynchronous IO,异步非阻塞式IO模型)

1 Linux的五种IO模型

五种IO模型包括:阻塞IO、非阻塞IO、IO多路复用、信号驱动IO、异步IO

1.1 模型调用的函数

使用到的部分函数

1.1.1 recv函数

recv函数用于从套接字(socket)接收数据。在TCP/IP协议栈中,当数据从网络到达时,它首先被放入内核的接收缓冲区中。然后,应用程序可以通过调用recv函数将这些数据从内核缓冲区复制到用户空间的缓冲区中,以便进一步处理。

参数和返回值

  1. 参数:
    套接字文件描述符:指定要接收数据的套接字。
    缓冲区指针:指向用户空间缓冲区的指针,用于存放接收到的数据。
    缓冲区大小:指定用户空间缓冲区的大小(以字节为单位)。
    标志位:用于设置接收数据的特定方式(如是否阻塞、是否立即返回等)。
  2. 返回值:
    成功时:返回实际接收到的字节数。
    失败时:返回-1,并设置全局变量errno以指示错误类型。

1.1.2 select函数

在IO模型中,select函数是一个非常重要的系统调用,它允许一个程序同时监视多个文件描述符(通常是套接字描述符),以查看它们中的任何一个是否可以进行I/O操作(例如读、写或异常条件)。select函数在多种编程场景中都非常有用,特别是在需要处理多个并发连接的网络服务器中。

select函数通过三个文件描述符集(读集、写集和异常集)来监视多个文件描述符。调用select时,程序指定这三个集合,select函数将阻塞(除非设置了非阻塞标志),直到以下情况之一发生:

  • 指定的读集合中的一个或多个文件描述符变为可读(例如,有数据可以读取)。
  • 指定的写集合中的一个或多个文件描述符变为可写。
  • 指定的异常集合中的一个或多个文件描述符发生异常条件。

当select返回时,它将更新这三个集合,以反映哪些文件描述符已准备好进行I/O操作。

select系统调用允许程序同时在多个底层文件描述符上,等待输入的到达或输出的完成。以数组形式存储文件描述符,64位机器默认2048个。当有数据准备好时,无法感知具体是哪个流OK了,所以需要一个一个的遍历,函数的时间复杂度为O(n)。

参数和返回值

  1. 参数:
    nfds:要监视的文件描述符集合中的最大文件描述符加1。
    readfds:指向文件描述符集合的指针,这些文件描述符被监视以查看它们是否可读。
    writefds:指向文件描述符集合的指针,这些文件描述符被监视以查看它们是否可写。
    exceptfds:指向文件描述符集合的指针,这些文件描述符被监视以查看是否发生异常条件。
    timeout:指定select函数等待I/O操作发生的最长时间。如果为NULL,则select将无限期地等待。
  2. 返回值
    返回值是准备好的文件描述符的总数。如果返回-1,则表示发生了错误。

1.1.3 poll函数

poll函数通过轮询的方式检测输入源(文件描述符)是否有数据到达或是否准备好进行I/O操作。它会遍历一个由pollfd结构体组成的数组,每个结构体包含了一个文件描述符、要监视的事件类型以及实际发生的事件类型。当调用poll函数时,它会阻塞(除非设置了非阻塞标志),直到指定的文件描述符之一准备好进行I/O操作或超时。

poll函数使用pollfd结构体来指定要监视的文件描述符和事件类型。pollfd结构体的定义通常如下:

java">struct pollfd {int fd;         // 文件描述符short events;   // 要监视的事件类型(如读、写、异常等)short revents;  // 实际发生的事件类型,由内核设置
};

以链表形式存储文件描述符,没有长度限制。本质与select相同,函数的时间复杂度也为O(n)。

参数和返回值

  1. 函数
int poll(struct pollfd *fds, unsigned long nfds, int timeout);
  1. 参数
    fds:指向pollfd结构体数组的指针。
    nfds:数组中pollfd结构体的数量。
    timeout:指定poll函数等待I/O操作发生的最长时间(以毫秒为单位)。如果为-1,则poll将无限期地等待;如果为0,则poll将立即返回,不阻塞。

  2. 返回值
    返回-1:表示发生错误。
    返回0:表示在指定的时间内没有任何事件发生。
    返回正数:表示有事件发生,返回值是准备好进行I/O操作的文件描述符的数量。

1.1.4 epoll函数

epoll的工作原理主要基于Linux内核中的高效数据结构和事件驱动机制。事件驱动的,即如果某个流准备好了,会以事件通知,知道具体是哪个流,因此不需要遍历,函数的时间复杂度为O(1)。

数据结构:

  1. 红黑树:epoll在内核中使用红黑树来管理所有注册的文件描述符(通常是socket)。红黑树是一种平衡二叉搜索树,它的查找、插入和删除操作的时间复杂度都是O(log n),这使得epoll能够高效地管理大量的文件描述符。
  2. 就绪列表:epoll还维护了一个就绪列表,用于存储那些已经就绪、有事件发生的文件描述符。就绪列表通常使用双向链表来实现,因为双向链表支持快速的插入和删除操作。当有事件发生时,内核会将相应的文件描述符从红黑树中取出,并加入到就绪列表中。

工作原理:

  1. 创建epoll实例:
    使用epoll_create函数创建一个epoll实例,该函数返回一个文件描述符,用于后续操作。
  2. 注册事件:
    使用epoll_ctl函数将感兴趣的文件描述符(通常是socket)及其事件(如读就绪、写就绪等)注册到epoll实例中。这一步可以添加新的事件、修改已注册的事件或删除事件。
  3. 等待事件:
    使用epoll_wait或epoll_pwait函数等待事件的发生。这些函数会阻塞调用线程,直到有注册的事件发生或超时。当事件发生时,函数会返回发生事件的文件描述符数量,并将事件信息存储在提供的epoll_event结构体数组中。

1.1.5 sigaction函数

在I/O模型中,特别是在信号驱动I/O模型中,sigaction函数扮演着重要的角色。信号驱动I/O模型是Unix/Linux系统中一种处理I/O操作的方式,它使用信号来通知应用程序数据已经准备好可以进行处理。在这个过程中,sigaction函数被用来设置信号处理函数,以便在接收到特定信号(如SIGIO)时执行相应的操作。

sigaction在信号驱动I/O模型中的作用

  1. 设置信号处理函数:
    在信号驱动I/O模型中,应用程序首先需要通过sigaction函数设置一个信号处理函数。这个处理函数将在接收到SIGIO信号时被调用。SIGIO信号通常用于通知应用程序某个文件描述符(如套接字)上有数据到达或可以进行I/O操作。
  2. 配置文件描述符:
    除了设置信号处理函数外,应用程序还需要将相关的文件描述符(如套接字)配置为非阻塞模式,并使其能够接收SIGIO信号。这通常通过fcntl函数来实现,设置文件描述符的O_ASYNC和O_NONBLOCK标志。
  3. 接收并处理信号:
    当数据到达配置为信号驱动I/O模式的文件描述符时,内核会发送SIGIO信号给应用程序。应用程序的信号处理函数随后被调用,可以在该函数中执行读取数据或其他相关操作。

1.2 IO模型

1.2.1 阻塞IO模型

在这里插入图片描述
进程发起IO系统调用后,进程被阻塞,转到内核空间处理,整个IO处理完毕后返回进程。操作成功则进程获取到数据。

1.2.2 非阻塞IO模型

在这里插入图片描述
进程发起IO系统调用后,如果内核缓冲区没有数据,需要到IO设备中读取,进程返回一个错误而不会被阻塞;进程发起IO系统调用后,如果内核缓冲区有数据,内核就会把数据返回进程。

对于上面的阻塞IO模型来说,内核数据没准备好需要进程阻塞的时候,就返回一个错误,以使得进程不被阻塞。

1.2.3 IO复用模型

在这里插入图片描述
多个的进程的IO可以注册到一个复用器(select)上,然后用一个进程调用该select, select会监听所有注册进来的IO;

如果select没有监听的IO在内核缓冲区都没有可读数据,select调用进程会被阻塞;而当任一IO在内核缓冲区中有可数据时,select调用就会返回;

相比于阻塞IO模型,多路复用只是多了一个select/poll/epoll函数。select函数会不断地轮询自己所负责的文件描述符/套接字的到达状态,当某个套接字就绪时,就对这个套接字进行处理。select负责轮询等待,recvfrom负责拷贝。当用户进程调用该select,select会监听所有注册好的IO,如果所有IO都没注册好,调用进程就阻塞。

1.2.4 信号驱动IO模型

在这里插入图片描述
模型也分为两个阶段:
数据准备阶段:未阻塞,当数据准备完成之后,会主动的通知用户进程数据已经准备完成,对用户进程做一个回调。
数据拷贝阶段:阻塞用户进程,等待数据拷贝。

1.2.5 异步IO模型

在这里插入图片描述
当进程发起一个IO操作,进程返回(不阻塞),但也不能返回果结;内核把整个IO处理完后,会通知进程结果。如果IO操作成功则进程直接获取到数据。

1.2.6 IO模型比较

在这里插入图片描述

2 Java的BIO、NIO、AIO

2.1 BIO(Blocking IO,同步阻塞式IO模型)

工作机制:在BIO模型中,每个客户端连接都会在一个独立的线程中处理。这个线程在处理IO操作时会阻塞,直到操作完成。因此,每个连接都需要一个独立的线程。当连接数较多时,会消耗大量的内存和CPU资源。
应用场景:适用于连接数少的场景。在这种情况下,程序编写相对简单,但对服务器的资源要求较高。在JDK 1.4之前,BIO是Java中唯一的IO模型选择。

2.2 NIO(Non-blocking IO,同步非阻塞式IO模型)

核心组件:NIO包含三大核心组件,即通道(Channel)、缓冲区(Buffer)和选择器(Selector)。
工作机制:在NIO模型中,一个线程可以处理多个连接。客户端连接请求会注册到多路复用器(Selector)上。多路复用器检测到某个连接有IO事件(如读、写、连接等)时,就会处理该事件。这种非阻塞模式使得主线程在未发生数据读写事件时无需阻塞,可以继续执行其他任务,从而增强了服务器的并发处理能力。
应用场景:适用于连接数多的场景,如聊天服务器、服务器间通讯等。然而,由于NIO的编程模型相对复杂,因此程序编写难度较高。NIO从JDK 1.4版本开始被支持。

2.3 AIO(Asynchronous IO,异步非阻塞式IO模型)

核心组件:AIO引入了异步通道的概念,并使用Future或CompletionHandler来处理异步操作的结果。
工作机制:在AIO模型中,读写异步通道会立刻返回,而不需要等待IO操作完成。读写的数据由Future或CompletionHandler进一步处理。当操作系统完成IO操作后,会主动通知应用程序,或者调用应用程序注册的回调函数来处理结果。
应用场景:也适用于连接数多的场景,但更加偏向于异步操作多的场景。AIO作为NIO的改进和增强,随JDK 1.7版本更新被集成在JDK的nio包中,因此也被称为NIO 2.0。

细化文章推荐:Java的BIO、NIO、AIO


http://www.ppmy.cn/ops/160965.html

相关文章

985硕研一无人机方向转嵌入式可能吗?如何选择未来方向?

今天给大家分享的是一位粉丝的提问,985硕研一无人机方向转嵌入式可能吗?如何选择未来方向? 接下来把粉丝的具体提问和我的回复分享给大家,希望也能给一些类似情况的小伙伴一些启发和帮助。 同学提问: 老师我是985硕研…

STM32的HAL库开发---多通道ADC采集(DMA读取)实验

一、实验介绍 1、功能描述 通过DMA读取数据 通过ADC1通道0/1/2/3/4/5(PA0/1/2/3/4/5)采集测试电压,并显示ADC转换的数字量及换算后的电压值 2、确定最小刻度 VREF 3.3V ---> 0V ≤ VIN ≤ 3.3V --->最小刻度 3.3 / 4096 &#x…

计算机毕业设计SpringBoot+Vue.js个性化图书推荐系统(源码+LW文档+PPT+讲解+开题报告)

温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…

CodeGPT 使用教程(适用于 VSCode)

CodeGPT 使用教程(适用于 VSCode) CodeGPT 是一个 VSCode 插件,可以让你在代码编辑器中直接调用 GPT 进行代码补全、优化、调试等操作。以下是详细的安装和使用步骤: 1. 安装 CodeGPT 方式 1:从 VSCode 插件市场安装…

Redis 缓存穿透、击穿、雪崩:问题与解决方案

在使用 Redis 作为缓存中间件时,系统可能会面临一些常见的问题,如 缓存穿透、缓存击穿 和 缓存雪崩。这些问题如果不加以解决,可能会导致数据库压力过大、系统响应变慢甚至崩溃。本文将详细分析这三种问题的起因,并提供有效的解决…

抓包工具 wireshark

1.什么是抓包工具 抓包工具是什么?-CSDN博客 2.wireshark的安装 【抓包工具】win 10 / win 11:WireShark 下载、安装、使用_windows抓包工具-CSDN博客 3.wireshark的基础操作 Wireshark零基础使用教程(超详细) - 元宇宙-Meta…

重订货点和安全库存

重订货点 重订货点是指当库存水平下降到某个特定值时,系统会自动触发采购或生产订单。其目的是确保在物料消耗完之前,能够及时补充库存。 安全库存 安全库存是为应对未来物资供应或需求的不确定性因素(如突发性订货、交货期突然延期等&…

算法的复杂性分析以及时间复杂度的表示方法

算法复杂性是算法运行所需的计算机资源量。 需要的时间资源的量称为时间复杂度,TT(N,I) 需要的空间资源的量称为空间复杂度,TT(N,I) N代表问题的规模,I代表输入(实例) 空间复杂度与时间复杂度的分析方法类同&#…