从0开始认识IO流

1、IO 的概念

  • 输入 (Input):系统或设备接收的数据。例如,键盘输入的字符、网络接收的数据包,或磁盘读取的文件内容等。
  • 输出 (Output):系统或设备输出的数据。例如,屏幕上显示的文本、网络发送的数据包,或写入磁盘的文件内容等

2、IO的示例

  1. 文件 IO:对文件的读写操作。
  2. 网络 IO:在网络连接中发送和接收数据。
  3. 设备 IO:对硬件设备(如硬盘、键盘、显示器等)进行的读写操作。
  4. 涉及计算机核心与其他设备间数据迁移的过程,就是IO。

3、IO常见问题

IO 操作可能受到硬件的物理限制,因而通常是系统性能的瓶颈之一

4、IO的作用范围

操作系统分为:

用户空间:用户访问的区域

+ 内核空间:操作系统访问的区域

进程(应用程序)在用户空间运行:

  1. 应用程序的 IO 请求:应用程序会向操作系统发起 IO 请求(例如,文件读取、网络请求等)。这些请求会通过系统调用进入内核空间。
  2. IO 在内核中执行:真正的 IO 操作(如磁盘读写、网络数据传输等)是在操作系统内核中进行的。操作系统负责将应用程序的请求转化为具体的硬件操作。IO 的过程本质上是在内核空间中执行的,而不是在用户空间。
  3. 用户空间的 IO:虽然应用程序自己不直接执行 IO 操作,但它通过调用操作系统提供的接口来发起 IO 请求。因此,可以说,用户空间并不直接进行 IO,而是通过操作系统来间接执行这些操作。

总结: 用户空间不存在IO,但是会发送IO请求给内核空间,然后就需要内核空间进行IO操作

5、IO的几种情形

1、同步IO

概念:在此情况下,程序会等待 IO 操作完成后再继续执行。

实现方式:直接写代码即可

2、异步IO

概念:程序在发起 IO 请求后无需等待 IO 完成,可以继续执行其他任务。IO 完成后,系统会通知程序,或让程序在空闲时自行检查 IO 状态。这种方式提高了系统资源利用率和并发性能

实现方式:new thread() 实现runable接口 future接口的实现(异步编排)线程池 消息队列

3、阻塞IO

概念:在阻塞 IO 模式中,程序在发起 IO 操作后会被“阻塞”,即停止执行其他操作,直到 IO 操作完成。

常用于 IO很快的操作 或则 不在乎快速返回结果的操作

4、非阻塞IO

概念:在非阻塞 IO 模式中,程序发起 IO 操作后,不会等待 IO 操作完成,而是立即返回。程序可以继续执行其他任务,随时检查 IO 状态或等待系统通知 IO 操作的完成。

(所以常与轮询啊,事件通知等可以检测到IO操作是否完成的机制结和使用)

综合:

同步阻塞IO :直接使用 代码实现IO操作

同步非阻塞IO:创建一个监听器,监听io完成状态,执行完操作,等待io操作完成返回结果

异步阻塞IO:使用异步返回请求,但是异步线程的执行需要等IO完成后才能结束(线程池+io操作)

异步非阻塞IO:使用异步的操作,然后搞个监听器,监听管道的状态,然后一旦完成就回调我们的函数(nio包)

5、IO多路复用

IO 多路复用是一种高效的 IO 模型,允许一个线程同时监视多个 IO 操作。它的核心思想是让操作系统帮助检测哪些 IO 资源已经准备好进行读写,而不是让每个 IO 操作都单独等待。

常用的 IO 多路复用方法

select

select 是最早实现 IO 多路复用的系统调用,通过一个文件描述符集合来监控多个 IO 通道。

它的调用会阻塞进程,直到一个或多个文件描述符变为就绪(可读、可写或有异常发生),然后程序可以对就绪的文件描述符执行 IO 操作。

缺点在于文件描述符集合的大小有限,且需要遍历所有的文件描述符,性能较低。

select 是早期的 I/O 多路复用机制,它使用 一个固定大小的集合 来存储文件描述符。这个集合实际上是一个位图(bit map),每个文件描述符都对应一个位(bit),该位表示文件描述符的状态。

文件描述符的最大限制:select 要使用一个固定大小的数组来存储所有的文件描述符,最大容量由 FD_SETSIZE 限制,通常在 Linux 系统中,FD_SETSIZE 的默认值是 1024。也就是说,select 最多可以同时监视 1024 个文件描述符。这是因为在 select 中,文件描述符是通过一个固定大小的位图来表示的,每个文件描述符对应一个位。如果需要支持更多的文件描述符,就需要增加位图的大小,但在实际操作系统中,FD_SETSIZE 通常限制了这一点。

位图的限制:select 内部使用的位图通常是在栈上分配的内存,这限制了可以存储的文件描述符数量。如果文件描述符超过了 FD_SETSIZE,就无法处理更多的连接。

性能问题:当文件描述符的数量非常多时,select 需要在集合中逐个检查每个文件描述符,这样的操作是线性时间复杂度 (O(n)) 的,导致了性能的瓶颈。尤其是当连接数非常大时,性能会急剧下降。

poll

poll 是 select 的改进版,没有文件描述符数量限制,解决了 select 的一些不足。

poll 通过一个数组而非集合来管理文件描述符,但仍然需要轮询所有文件描述符,且每次调用都需要重新构建文件描述符数组。

随着文件描述符数量增加,poll 的性能会下降。

数组可以扩容,只受内存资源的影响

epoll

epoll 是 Linux 下的高效 IO 多路复用方式,针对 select 和 poll 的性能问题进行了优化。

epoll 的文件描述符不需要重复传递,且支持“边缘触发(edge-triggered)”和“水平触发(level-triggered)”两种模式,能够更高效地检测文件描述符的状态变化。

它适合处理大量并发连接,被广泛用于高并发服务器(如 Nginx、Redis)中。

redis的多路IO复用

假设有一个客户端连接到 Redis 并发送请求:

  1. 客户端连接建立:Redis 启动并进入事件循环,监听端口,等待客户端连接。
  2. 客户端发送请求:客户端发送一个请求(比如 SET key value)。
  3. 事件循环检测到 I/O 事件:Redis 的事件循环使用 I/O 复用机制(例如 epoll)监听网络套接字的变化,当发现客户端有请求数据时,触发 read() 操作。
  4. 回调处理请求:读取到客户端的请求数据后,Redis 将请求交给回调函数进行解析和处理(例如 SET 命令的处理)。
  5. 生成响应并发送给客户端:完成命令处理后,Redis 将响应数据通过网络写回客户端。
  6. 事件循环继续:事件循环继续等待新的 I/O 事件,直到下一个请求到达。

6、事件驱动模型

概念:事件驱动模型通过将 I/O 操作和程序的逻辑解耦,允许程序在等待 I/O 操作时继续执行其他任务,而不必阻塞或轮询每个 I/O 操作。

实现流程:非阻塞 I/O 操作:程序发起一个 I/O 请求(例如读取文件或从网络获取数据),但不阻塞等待 I/O 完成。该操作会立即返回,而不是停下来等待数据。

注册事件和回调函数:程序将感兴趣的事件(如数据可读、连接建立、数据可写等)和相应的回调函数注册到事件驱动框架中。回调函数会在事件发生时被调用。

事件循环:事件循环会持续检查是否有 I/O 事件发生。一旦事件发生,事件循环会触发相关的回调函数,处理 I/O 结果。

回调函数执行:当 I/O 事件准备就绪时(如网络数据到达、文件准备就绪等),事件驱动框架会调用之前注册的回调函数。回调函数通常是处理事件的逻辑,如读取数据、处理请求、发送响应等。

继续执行其他任务:事件循环不会因为等待某个 I/O 操作的完成而阻塞,程序可以继续执行其他任务,直到某个 I/O 事件触发回调函数。

实现网络通信的架构方式:

1、线程架构模式:一个io一个线程

2、事件驱动模式:reactor堆反应模式

1、一个线程:io 和cpu 调度都有这个线程执行 最经典的实现场景 redis中的多路io复用
2、一个线程+线程池:
3、多个reator线程+线程池:

实现io通信的模式

1、Bio

同步阻塞IO

2、Nio

取决于IO的操作是同步或则异步,同步就需要轮询得到IO结果,同步非阻塞IO,异步就异步非阻塞IO

3、Aio

完全异步的操作,由内核线程或自定义线程池实现异步,事件回调的方式实现IO

实现多路复用的机制

1、Select

遍历所有文件,将需要IO的文件存入固定值大小的集合(1024)位图思想

2、Poll

遍历所有文件,将需要IO的文件存入动态数组

3、Epoll

由内核实现,需要IO请求实现事件驱动机制,不再是遍历获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值