当前位置: 首页 > news >正文

IO(Input/Output)

IO

IO,即输入/输出,磁盘IO,网络IO

计算机角度的IO

主观意思就是计算机输入输出,计算机是主体。计算机分为5个部分:运算器,控制器,存储器,输入设备,输出设备。

 输入设备:向计算机输入数据和信息的设备,键盘,鼠标都属于输入设备;

输出设备:计算机硬件系统的终端设备,用于接收计算机数据的输出显示,一般显示器、打印机都属于输出设备。

操作系统角度的IO

要将内存中的数据写入磁盘时,那么主体就是一个程序,操作系统负责计算机的资源管理和进程调度我们电脑上运行的应用程序,其实就是需要经过操作系统,才能做一些特殊操作,像磁盘文件的读写内存的读写等。

整整意义上的IO是在操作系统执行的,即应用程序的IO操作氛围两种动作:IO调用和IO执行。IO调用是由进程(应用程序运行态)发起的,而IO执行是操作系统内核的工作。

应用程序发起一次IO的操作流程:

IO调用:应用程序进程向操作系统内核发起调用

IO执行:操作系统内核完成IO操作

  • 内核(Kernel):操作系统核心,管理硬件资源和 IO 操作。

  • 用户空间(User Space):应用程序运行的内存区域,通过系统调用与内核交互。

  • 缓冲区(Buffer):临时存储数据的内存区域,减少直接操作设备的次数。

IO模型 

阻塞IO(Blocking IO)

        用户线程发起 IO 操作后被阻塞,直到数据就绪并完成拷贝。

        缺点如果内核数据一直没准备好,那用户进程将一直阻塞,浪费性能

                   (线程长时间阻塞,资源利用率低)

  • 流程

    1. 用户线程调用 read()

    2. 内核等待数据就绪(如网络包到达)。

    3. 数据从内核空间拷贝到用户空间。

    4. 用户线程恢复执行。

非阻塞IO(Non-blocking IO)

        户线程轮询检查数据是否就绪,未就绪时立即返回错误.

        缺点:频繁轮询消耗 CPU

  • 流程

    1. 用户线程调用 read(),若数据未就绪,内核返回 EWOULDBLOCK

    2. 线程继续执行其他任务,定期重试。

    3. 数据就绪后,完成拷贝。

IO多路复用(IO Multiplexing)

        特点:通过 Selector 监控多个 IO 通道,仅当某通道数据就绪时通知线程处理。

        实现机制系统给我们提供一类函数(如 select、poll、epoll), 它们可以同时监控多个 fd的操作,任何一个返回内核数据就绪,应用进程再发 起 recvfrom()系统调用。

        select/poll(轮询)、epoll(事件驱动,Linux 高效实现)。

select

应用进程通过调用select函数,同时监控多个fd,只要有一个数据状态准备就绪,select函数就会返回可读状态,这时应用进程再发起recvfrom()请求去读取数据

文件描述符 fd(File Descriptor):用来表示相关文件信息
  • 流程

    1. 注册多个通道到 Selector。

    2. Selector 阻塞等待至少一个通道就绪。

    3. 处理就绪通道的 IO 操作。

select 的 IO 多路复用模型,只需要发起一次询问就够了,大大优化了性能。 

缺点:监听的最大连接数有限,在 Linux 系统上一般为 1024kb,select 函数返回后,是通过遍历 fdset,找到就绪的描述符 fd。

因为存在大量遍历,所以会有连接数限制,后面提出poll,与select相比,解决了连接数限制问题,但是同样需要遍历FD,如果就绪fd很少,连接数又很大,那么效率就会线性下降。

epoll

为解决select/poll存在的问题,epoll诞生,它采用时间驱动来实现,

epoll通过epoll_ctl()来注册一个fd,一旦基于某个fd就绪,内核就会采用回调机制,迅速激活这个fd,当进程调用epolll_wait()时便能得到通知,这里去掉了遍历fdset的操作,而是采用监听事件回调的机制。

特性selectpollepoll
实现机制轮询遍历所有 fd轮询遍历所有 fd事件驱动(回调通知就绪 fd)
数据结构位数组(fd_set链表(pollfd结构体数组)红黑树 + 就绪链表
最大 fd 数量1024(受 FD_SETSIZE 限制)无限制(由系统资源决定)无限制(由系统资源决定)
时间复杂度O(n)O(n)O(1)(仅处理就绪 fd)
内存拷贝开销每次调用需拷贝 fd 集合到内核同 select首次注册后,内核维护 fd 集合无需拷贝

异步 IO(AIO asynchronous IO) 

        用户线程发起 IO 操作后立即返回,内核完成数据拷贝后通知线程

  • 流程

    1. 用户线程调用 aio_read()

    2. 内核负责等待数据就绪并拷贝到用户空间。

    3. 内核通过回调或信号通知线程处理数据。

  • 优点:真正异步,无任何阻塞。

 一个场景理解BIO,NIO,AIO

        A,B,C三人去窗口排队买饭,

        BIO:A一直在排队,直到排到自己。

        NIO:B一边玩游戏一边排队,玩一会瞅一下是不是到自己了,直至排到自己。

        AIO:C直接告诉老板,好了叫一下我。

JAVA IO 

阻塞 IO(Blocking I/O BIO)

服务器为每一个链接建立一个线程,由该线程单独负责处理一个客户请求。

  • 特点:每个连接对应一个线程,线程阻塞等待 IO。

  • 适用场景:低并发、简单应用。

  • // 服务端
    ServerSocket server = new ServerSocket(8080);
    while (true) {
        Socket client = server.accept(); // 阻塞
        new Thread(() -> handle(client)).start();
    }

非阻塞IO( non-blocking IO NIO)

NIO 支持面向缓冲区的、基于通道的 IO 操作。

核心思想: NIO 中非阻塞 I/O 调用不会被阻塞,核心是注册感兴趣的特定 I/O 事件,如可读数据到达,新的套接字连接等等,在发生特定事件时,系统再通知我们,NIO 中实现非阻塞 I/O 的核心对象就是 Selector.

  • 核心类ChannelBufferSelector

  • 适用场景:高并发网络服务。

  • 当有读或写等任何注册的事件发生时,可以从 Selector 中获
    得相应的 SelectionKey,同时从 SelectionKey 中可以找到发生的事件和该事
    件所发生的具体的 SelectableChannel,以获得客户端发送过来的数据。
非阻塞指的是 IO 事件本身不阻塞,但是获取 IO 事件的 select()方法是
需要阻塞等待的.
区别是 BIO 会阻塞在 IO 操作上,NIO 阻塞在事件获取上,没有事件就没有
IO,从高层次看 IO 就不阻塞了.

Channels

通道,与stream不同,stream是单向的,而channels是双向的,就可以读,又可以写。

所有数据都通过 Buffer 对象来处理。你永远不会将字节直接写入通道中,相反,您是将数据写入包含一 个或者多个字节的缓冲区。同样,您不会直接从通道中读取字节,而是将数据从 通道读入缓冲区,再从缓冲区获取这个字节。

 

NIO 中的 Channel 的主要实现有:
FileChannel:从文件中读写数据
DatagramChannel:通过 UDP 读写网络中的数据
SocketChannel:通过 TCP 读写网络中的数据
ServerSocketChannel:可以监听新进来的 TCP 连接
常用方法:
流对象.getChannel(); 通过流对象获得管道
read(byteBuffer); 将数据导入缓存数组
write(byteBuffer); 将数据从缓存数组写出

Buffers

缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存,这块内存被包装成 NIO Buffer 对象,并提供了一组方法,用来方便的访问该块内存。
对数据的读取/写入需要使用 buffer,buffer 本质就是一个数组

常用方法:

ByteBuffer.allocate(1024); 创建字节数据
byteBuffer.flip(); 翻转这个缓冲区,读操作前使用
byteBuffer.clear(); 清除缓存,写操作前使用
    public static void main(String[] args) throws IOException {
        //输入文件
        FileInputStream in = new FileInputStream("D:/source.txt");
        //输出文件
        FileOutputStream out = new FileOutputStream("D:/dest.txt");
        //创建通道
        FileChannel  inchannel =  in.getChannel();
        FileChannel  outchannel =  out.getChannel();
        //创建缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
          while(inchannel.read(byteBuffer)!=-1){
              //切换读,保护已写数据
              byteBuffer.flip();
              //写入
              outchannel.write(byteBuffer);
              //切换写
              byteBuffer.clear();
          }
    }

Selectors 

用于检查一 个或多个 NIO Channel(通道)的状态是否处于可读、可写。如此可以实现单线程管理多个 channels,也就是可以管理多个网络链接
优点:使用更少的线程来就可以来处理通道了,避免了线程上下文切换带来的开销

 异步 IO(AIO asynchronous IO) 

基于回调机制Future模式,允许应用程序在 I/O 操作完成后通过事件通知处理结果,无需阻塞线程

核心思想:将 I/O 操作的发起与完成解耦,主线程仅发起 I/O 请求,内核或后台线程负责完成实际的数据传输并通知结果。

异步流程(以文件读取为例)
  1. 发起异步操作:调用 read() 方法,传入 CompletionHandler

  2. 内核执行 I/O:数据从磁盘读取到内核缓冲区

  3. 数据拷贝:内核将数据拷贝到用户空间缓冲区。

  4. 回调通知:通过 CompletionHandler 的 completed() 或 failed() 通知结果。

相关文章:

  • GStreamer —— 3.1、Qt+GStreamer制作多功能播放器,支持本地mp4文件、rtsp流、usb摄像头等(可跨平台,附源码)
  • 新增菜品-02.代码开发2
  • 【后端】【Djagno】【ORM】models.ManyToManyField 多对多字段类型全解
  • 【设计模式】策略模式
  • EasyExcel--导入和导出Excel的方法
  • 面向机器人领域 | AKM Delta-Sigma数字输出无磁芯电流传感器
  • turnjs图册翻书效果
  • 数据仓库是什么,跟数据集成有什么关系
  • Web3 时代数据保护的关键挑战与应对策略
  • PostgreSQL 触发器
  • 电机控制常见面试问题(十四)
  • gralloc1_perform具体在干什么
  • 从两指到三指:Robotiq机器人自适应夹持器技术解析
  • MySQL InnoDB 事务隔离级别和锁
  • git 命令回退版本
  • nodejs - 基础知识
  • Plant Simulation中怎么更改机器人3D模型
  • 精挑20题:MySQL 8.0高频面试题深度解析——掌握核心知识点、新特性和优化技巧
  • WPF 布局中的共性尺寸组(Shared Size Group)
  • Git远程拉取和推送配置
  • 搜狐一季度营收1.36亿美元,净亏损同比收窄超两成
  • 4天内,云南昆明又一县市区原主官被查
  • 复旦兼职教授高纪凡首秀,勉励学子“看三十年才能看见使命”
  • 著名文学评论家、原伊犁师范学院院长吴孝成逝世
  • LPR名副其实吗?如果有所偏离又该如何调整?
  • 从《缶翁的世界》看吴昌硕等湖州籍书画家对海派的影响