IO线程模型

您所在的位置:网站首页 read非阻塞读文件 IO线程模型

IO线程模型

2023-06-10 00:36| 来源: 网络整理| 查看: 265

注:本文内容部分摘自《Netty 4 核心原理与手写 RPC 实践》

1. IO 简介

在 UNIX 的设计哲学中,一切皆文件。文件就是一串二进制流。对文件的操作包括有:读、写、打开、关闭 等。计算机对这些流进行数据的收发操作,简称为 IO(Input and Output)操作。 IO 的种类:内存 IO、网络IO、磁盘IO。

2. IO 模型 2.1 基本概念 2.1.1 同步 vs 异步

同步和异步是指 CPU 时间片的使用,主要看请求发起方对消息结果的获取是主动发起的,还是被动通知的。如果是服务方在处理完成后主动告知请求方,那么就是异步。异步通知的方式一般通过状态改变、消息通知或者回调函数来完成,大多时候采用的都是回调函数。

2.1.2 阻塞 vs 非阻塞

阻塞和非阻塞通常指对 IO 的操作。阻塞就是当我们发起一个请求调用时,在请求结果返回之前,如果当前线程是处于挂起状态,那么就是阻塞。如果是运行状态,那么就是非阻塞。

2.2 IO 模型 2.2.1 同步阻塞

应用程序发起调用 recvfrom -> 内核准备数据->数据就绪->数据从内核拷贝到用户空间。

特点在 I/O 执行的两个阶段(等待数据和拷贝数据)都被阻塞典型应用Java BIO 、阻塞 Socket优点1. 进程阻塞挂起不消耗 CPU 资源 2. 实现难度低 3. 适合并发量小的网络应用开发缺点1. 不适合并发量大的应用,因为一个请求 I/O 会阻塞进程 2. 需要为每个请求分配一个处理进程(线程)以及时响应,系统开销大

在阻塞 I/O 的场景中,当调用 InputStread.read() 方法时是阻塞的,它会一直等到数据到来(或超时)时才会返回;同样,在调用 SeverSocket.accept() 方法时,也会一直阻塞到有客户端连接才会返回,每个客户端连接成功后,服务端都会启动一个线程去处理该客户端的请求。 阻塞 I/O 的缺点:

当客户端多时,会创建大量的处理线程。且每个线程都要占用栈空间和一些 CPU 时间。阻塞可能带来频繁的上下文切换,且大部分上下文切换可能是无意义的。 在这里插入图片描述 2.2.2 同步非阻塞

应用程序发起调用 recvfrom -> 内核返回数据准备结果 -> 重复 recvfrom… -> 数据就绪->数据从内核拷贝到用户空间。 相较于同步阻塞,同步非阻塞会在被调用之后即刻返回一个结果。当用户进程发起 read 操作时,如果内核中的数据还没有准备好,那么它不会阻塞进程而是立刻返回一个 error。

特点用户进程需要不断地主动询问内核(Kernel)数据是否已准备好典型应用Socket 设置成 NON_BLOCK优点1. 实现难度,开发应用相较于阻塞 I/O 模型较难缺点1. 进程轮询(重复)调用,消耗 CPU 资源 2. 适合并发量较小且不需要及时响应的网络应用开发 2.2.3 IO 多路复用

多个进程可以注册到一个 Selector(复用器)上,当用户进程调用该 Selector,Selector 会监听注册进来的所有 I/O,如果 Selector 监听的所有 I/O 在内核缓冲区都没有可读数据,select 调用进程会被阻塞,而当任一 I/O 在内核缓冲区有可读数据时,select 调用就会返回,而后 select 调用进程可以自己或通知另外的进程再次发起读取 I/O,读取内核中准备好的数据。多个进程注册 I/O 后,只有一个 select 调用进程被阻塞。

特点对于每一个 Socket,一般都设置成非阻塞,但是整个用户的进程其实是一直被阻塞的,只不过进程是被 select 函数阻塞,而不是被 Socket I/O 阻塞典型应用Java NIO优点1. 专一进程解决多个进程 I/O 的阻塞问题,性能好,Reactor 模式 2. 适合高并发服务应用开发,一个进程/线程处理多个请求缺点1. 实现和开发应用难度大

目前流行的多路复用 I/O 的实现主要包括四种:select、poll、epoll、kqueue。 多路复用 I/O 技术最适用的是『高并发』场景,所谓『高并发』是指 1ms 内至少同时有上千个连接请求准备好。 在这里插入图片描述 Java NIO 是从 JDK 1.4 开始使用的。下面是 Java NIO 的工作原理:

由一个专门的线程来处理所有的 I/O 事件,并负责分发。事件驱动机制:事件到的时候触发,而不是同步地去监视事件。线程通信:线程之间通过 wait、notify 等方式通信。保证每次上下文切换都是有意义的,减少无谓的线程切换。 在这里插入图片描述 2.2.4 异步非阻塞

异步 IO 的工作机制:用户进程发起 aio_read 操作,给内核传递与 read 相同的描述符、缓冲区指针、缓冲区大小三个参数及文件偏移,告诉内核当整个操作完成时,如何通知我们立刻就可以开始去做其他的事。

特点真正实现了 异步 IO典型应用Java7 AIO优点1. 不阻塞,采用 Proactor 模式 2. 非常适合高性能、高并发服务应用缺点1. 实现和开发应用难度大 2. 需要操作系统的底层支持,Linux 2.5 内核首现,Linux 2.6 产品的内核标准特性 2.3 Java BIO 到 NIO 到 AIO IO 模型BIONIO通信面向流面向缓冲区处理阻塞非阻塞触发无选择器 2.3.1 面向流 vs 面向缓冲区 面向流:在面向流 IO 系统中,所有数据都是直接写入或者直接将数据读取到 Stream 对象中。每次从流中读取一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。面向缓冲区:在面向缓冲区 IO 系统中,所有数据都是用缓冲区处理的。在数据读取或写入时,都是直接读取或写入缓冲区。 在 NIO 中,所有的缓冲区类型都继承于抽象类 Buffer,最常用的是 ByteBuffer。 缓冲区实际上是一个容器对象,其本质是一个特殊的数组。缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化。在缓冲区种,最重要的属性有以下三个。 position:指定下一个要被写入或读取的元素索引。limit:指定还有多少数据需要取出/放入。capacity: 指定了可以存储在缓冲区中的最大数据容量。

在这里插入图片描述

2.3.2 阻塞 vs 非阻塞

阻塞:当一个线程调用 read() 或 write() 时,该线程被阻塞,直到有一些数据被读出,或者数据完全写入。该线程在此期间不能再做任何事情。 在这里插入图片描述

非阻塞:一个线程从某通道(Channel)发送请求读取/写入数据,这个线程同时可以去做其他事情。线程通常将非阻塞 IO 的空闲时间用于在其他通道上执行 IO 操作,所以一个单独的线程现在可以管理多个通道。

在这里插入图片描述

2.3.3 选择器

选择器(Selector)允许一个线程监听多个通道。可以注册多个通道使用一个选择器,然后使用一个单独的线程来选择通道。

NIO 可以只使用一个线程来管理多个通道(网络连接或文件)。 在这里插入图片描述

2.3.4 AIO

JDK 1.7 开始实现了真正的异步 AIO、把 IO 读写操作完全交给操作系统,学习了 Linux Epoll 模式。

在多路 IO 复用模型中,事件循环将文件句柄的状态事件通知给用户线程,由用户线程自行读取数据、处理数据。而在 AIO 模型中,当用户线程收到通知时,数据已经被内核读取完毕,并放在了用户线程指定的缓冲区内,内核在 IO 完成后通知用户线程直接使用即可。

AIO 模型使用 Proactor 设计模式实现这一机制。 在这里插入图片描述



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3