IO模型分类
简单了解
阻塞I/O
当用户程序执行read,线程被阻塞,等待两个过程,内核数据准备好和数据从内核态拷贝到用户态。read才返回
非阻塞I/O
非阻塞的read请求在还没准备好数据就返回,期间程序不断轮询内核直到数据准备完成,内核拷贝数据至应用程序缓冲区中,read调用才可以获得结果。
非阻塞的I/O多路复用
避免轮询期间没法做其他事。当内核数据准备好时,以事件通知应用程序进行操作。前半过程不需要程序持续主动去关注处理
上述三个为同步调用,过程都是需要等待
异步I/O
让内核数据准备好和数据从内核拷贝到用户态两个过程都不用等待。
发起aio_read后,立即返回,内核自动将数据从内核空间拷贝到应用程序空间,拷贝过程为异步,不需要主动发起拷贝动作。
完整细分
如阻塞 IO、非阻塞 IO、多路复用 IO、信号驱动 IO、异步 IO
-
阻塞 IO(Blocking I/O)
-
官方定义:在阻塞 IO 模型中,当进程(或线程)调用一个 IO 操作(如
read
或write
)时,该进程(或线程)会被阻塞,直到 IO 操作完成。这意味着在整个 IO 操作过程中,进程(或线程)会一直等待,不能执行其他任何操作。 -
主要特点:
-
简单直接:编程模型简单,易于理解和实现。开发人员不需要考虑复杂的 IO 状态检查和事件处理。
-
效率较低:在 IO 操作期间,进程(或线程)处于阻塞状态,不能同时处理其他任务。在高并发场景下,大量的线程可能会因为等待 IO 操作而被阻塞,导致资源浪费和性能下降。
-
-
主要原理和流程:
-
当进程(或线程)发起一个 IO 请求(如从文件或网络读取数据)时,操作系统会将这个请求放入内核的 IO 队列中。然后,进程(或线程)会进入阻塞状态,等待内核完成 IO 操作。一旦内核完成数据的读取或写入,它会将数据返回给进程(或线程),同时唤醒被阻塞的进程(或线程),使其继续执行后续的操作。
-
-
主要应用场景和用途:
-
简单的单线程应用:在一些简单的命令行工具或单线程的客户端 - 服务器通信中,当并发请求较少时可以使用。例如,一个简单的本地文件读取程序,只需要读取一个文件的内容,不涉及复杂的并发操作,使用阻塞 IO 就足够了。
-
传统的同步编程模型:在一些传统的、对性能要求不是特别高的同步编程场景中,如早期的简单网络应用,阻塞 IO 可以满足基本的需求。
-
-
-
非阻塞 IO(Non - blocking I/O)
-
官方定义:非阻塞 IO 是指当进程(或线程)调用一个 IO 操作时,如果 IO 操作不能立即完成(如没有数据可读或缓冲区已满无法写入),操作系统不会让进程(或线程)阻塞等待,而是返回一个错误码(表示 IO 操作暂时无法完成),进程(或线程)可以继续执行其他操作,然后在合适的时候再次发起 IO 请求。
-
主要特点:
-
需要轮询:由于 IO 操作不会阻塞进程(或线程),为了获取 IO 操作的结果,需要进程(或线程)不断地轮询(检查)IO 操作的状态,这增加了编程的复杂性。
-
部分提高并发性能:相比于阻塞 IO,非阻塞 IO 可以在等待 IO 操作完成的同时,利用等待时间执行其他任务,一定程度上提高了系统的并发性能,但轮询机制可能会导致 CPU 资源的浪费。
-
-
主要原理和流程:
-
当进程(或线程)发起 IO 请求时,操作系统会立即返回,如果 IO 操作可以立即完成(如缓冲区中有数据可读或有空间可写入),则返回数据或完成写入;如果 IO 操作不能立即完成,则返回一个表示操作暂时无法完成的错误码。进程(或线程)收到这个错误码后,可以先去执行其他任务,然后在后续的某个时间再次发起 IO 请求,直到 IO 操作完成。
-
-
主要应用场景和用途:
-
简单的高并发服务器雏形:在一些简单的高并发服务器设计的早期阶段,可以使用非阻塞 IO 来处理多个客户端的请求。例如,一个简单的网络服务器,通过不断地轮询多个客户端套接字,尝试读取数据,当有数据可读时进行处理,这样可以在一定程度上同时处理多个客户端的请求,但轮询机制可能会消耗大量的 CPU 资源。
-
结合其他机制提高性能:非阻塞 IO 可以和其他高效的 IO 机制(如多路复用 IO)结合使用,在多路复用的基础上,对于那些偶尔需要快速检查 IO 状态的情况,非阻塞 IO 可以提供更灵活的操作方式。
-
-
-
多路复用 IO(I/O Multiplexing)
比较重要更详细可看:
Select,poll,epoll和IO多路复用和NIO-CSDN博客
-
官方定义:多路复用 IO 是一种通过一个线程(或进程)来同时处理多个 IO 通道(如文件描述符、套接字等)的 IO 模型。它使用系统调用(如
select
、poll
、epoll
)来监听多个 IO 通道的状态,当其中任何一个通道有 IO 事件发生(如可读、可写)时,系统调用会返回,然后程序可以对相应的通道进行 IO 操作。 -
主要特点:
-
高效利用资源:通过一个线程(或进程)管理多个 IO 通道,减少了线程(或进程)的创建和销毁成本,同时避免了大量线程(或进程)阻塞等待的情况,能够高效地利用系统资源,提高系统的并发处理能力。
-
事件驱动编程:基于事件驱动,程序只需要关注有事件发生的 IO 通道,不需要像非阻塞 IO 那样不断地轮询每个通道,降低了 CPU 的占用率。
-
-
主要原理和流程:
-
首先,程序将需要监听的多个 IO 通道(通过文件描述符等表示)添加到多路复用器(如
select
、poll
或epoll
)中。然后,多路复用器会阻塞等待,当其中任何一个通道有 IO 事件发生(如网络套接字上有数据可读、可写或出现异常情况)时,多路复用器会返回,通知程序哪些通道有事件发生。程序收到通知后,就可以对相应的通道进行 IO 操作。
-
-
主要应用场景和用途:
-
高性能网络服务器:在大多数高性能的网络服务器(如 Nginx、Redis 等)中广泛应用。例如,Nginx 使用多路复用 IO 来同时处理大量的客户端连接请求,通过监听多个套接字的可读、可写事件,高效地处理 HTTP 请求和响应,实现高吞吐量和低延迟的网络服务。
-
需要同时处理多个 IO 源的系统:在任何需要同时处理多个文件、网络连接等 IO 源的场景下,如分布式系统中的节点间通信、数据库服务器处理多个客户端连接等,多路复用 IO 都可以提供高效的 IO 处理方式。
-
-
-
信号驱动 IO(Signal - driven I/O)
-
官方定义:信号驱动 IO 是一种 IO 模型,当一个描述符(如文件描述符或套接字)上的 IO 操作可以进行时(如数据到达可读或缓冲区可写),操作系统会向进程发送一个信号,进程收到信号后,再调用相应的 IO 操作函数来处理 IO 事件。
-
主要特点:
-
异步通知机制:与阻塞 IO 和非阻塞 IO 不同,信号驱动 IO 不需要进程(或线程)主动去检查 IO 操作的状态,而是由操作系统在 IO 事件发生时主动通知进程(或线程),这种异步通知机制可以提高程序的执行效率。
-
信号处理复杂性:信号驱动 IO 需要处理信号相关的复杂问题,如信号的捕获、信号处理函数的编写等。信号处理函数的执行上下文和程序的主流程是分开的,可能会涉及到一些并发安全和资源共享的问题。
-
-
主要原理和流程:
-
首先,进程需要使用系统调用(如
fcntl
)设置描述符的信号驱动属性,告诉操作系统当这个描述符上有 IO 事件发生时发送一个信号。当 IO 事件发生(如数据到达套接字)时,操作系统会向进程发送一个预先定义好的信号(如SIGIO
)。进程在收到信号后,会在信号处理函数中调用相应的 IO 操作函数(如read
或write
)来处理 IO 事件。
-
-
主要应用场景和用途:
-
实时性要求较高的系统:在一些对实时性要求较高的系统中,如实时数据采集系统、实时通信系统等,信号驱动 IO 可以在数据到达或可发送时及时通知程序进行处理,减少延迟。
-
需要及时响应 IO 事件的场景:当希望在 IO 事件发生的第一时间就进行处理,而不是通过轮询或其他方式延迟处理的场景下,信号驱动 IO 是一种选择。不过,由于信号处理的复杂性,它在实际应用中不如多路复用 IO 和异步 IO 广泛。
-
-
-
异步 IO(Asynchronous I/O)
-
官方定义:异步 IO 是一种 IO 模型,在这种模型中,当进程(或线程)发起一个 IO 操作时,操作会在后台自动进行,进程(或线程)不需要等待 IO 操作完成就可以继续执行其他任务。当 IO 操作完成后,操作系统会通过回调函数、事件通知或其他机制来告知进程(或线程)IO 操作已经完成。
-
主要特点:
-
真正的异步执行:与非阻塞 IO 和信号驱动 IO 相比,异步 IO 是真正的异步,进程(或线程)在发起 IO 操作后可以完全不用关心 IO 操作的进度,直到收到操作完成的通知,能够最大限度地提高系统的并发性能和资源利用率。
-
编程模型较复杂:异步 IO 的编程模型相对复杂,需要使用回调函数、事件循环等机制来处理 IO 完成后的操作,对开发人员的要求较高。
-
-
主要原理和流程:
-
进程(或线程)通过系统调用发起一个异步 IO 操作,操作系统会记录这个操作并在后台执行。在 IO 操作执行期间,进程(或线程)可以继续执行其他任务。当 IO 操作完成后,操作系统会根据程序设置的通知方式(如调用回调函数、发送事件通知等)告知进程(或线程),然后进程(或线程)可以在相应的回调函数或事件处理程序中处理 IO 操作的结果。
-
-
主要应用场景和用途:
-
高性能高并发系统的后端服务:在现代高性能高并发的后端服务(如 Node.js 服务器、一些高性能的分布式存储系统)中广泛应用。例如,Node.js 基于事件驱动和异步 IO 模型,可以高效地处理大量的并发网络请求,在处理数据库查询、文件读写等 IO 操作时,通过异步 IO 方式,使程序能够在等待 IO 操作完成的同时处理其他请求,提高系统的整体性能和响应速度。
-
需要处理大量并发 IO 操作的复杂应用:在一些需要处理大量并发的文件读写、网络通信等 IO 操作的复杂应用场景下,如大型的云计算平台、大数据处理系统等,异步 IO 可以提供高效的 IO 处理解决方案,充分利用系统资源,提高系统的吞吐量和并发处理能力。
-
-