Linux-IO模型

参考:

UNIX下的五种IO模型

10分钟看懂, Java NIO 底层原理

Linux 五种网络IO模式(阻塞IO、非阻塞IO、IO多路复用、信号驱动IO、异步IO)

1.什么是IO

根据冯.诺依曼结构,计算机结构分为 5 大部分:运算器、控制器、存储器、输入设备、输出设备。

image-20240530204905297

操作系统为了保证稳定性和安全性,一个进程的地址空间划分为 用户空间(User space)内核空间(Kernel space )

我们平常运行的应用程序都是运行在用户空间,只有内核空间才能进行系统态级别的资源有关的操作,比如文件管理、进程通信、内存管理等等。

并且,用户空间的程序不能直接访问内核空间。

现价,我们的用户程序,想要执行IO操作,由于没有执行这些操作的权限,只能发起系统调用请求操作系统帮忙完成。

例如,咱们在学校读书,为了保证安全,只能通过保安亭来和内部的同学交互。

现在我想找我弟,我只能找下保安大哥,麻烦帮我叫一下xx班的xx过来。

IO操作,实际上是发起系统调用,请求操作系统帮忙。

注意啊,这里分为2个步骤,准备数据拷贝数据

  • 准备数据:内核等待 I/O 设备准备好数据
  • 拷贝数据:内核将数据从内核空间拷贝到用户空间

就像下面这张图一样。

Trap是一种由软件触发的异常(Exception),用于从用户态(User Mode)切换到内核态(Kernel Mode)。

用户程序通过调用库函数(如readwrite等)发起系统调用,库函数会将系统调用号和参数传递给内核,并触发Trap。

image-20240530210356305

2.I/O模型

在UNIX系统中,I/O模型的分类主要包括五种类型,这些类型的差异在于数据如何从内核传输到用户空间,以及在此过程中程序的行为模式

I/O模型 描述 特点
同步阻塞I/O 程序发起I/O操作并等待或阻塞,直到数据准备就绪并处理完毕。 简单,易于理解和实现,但程序在等待I/O时无法执行其他任务。
同步非阻塞I/O 程序发起I/O操作,若数据未就绪,操作立即返回,程序可以做其他事情,但需不断检查数据是否就绪。 提高程序效率,但需要不断轮询检查,可能导致CPU使用率高。
信号驱动I/O 程序请求启动一个操作并立即返回,当数据准备好时由操作系统通过信号通知程序。 程序不需要持续检查数据是否就绪,提高效率,但处理信号有复杂性。
异步I/O 程序发起I/O操作后立即返回,整个I/O操作(包括数据准备和复制)由操作系统完成后通知程序。 完全不阻塞程序执行,程序继续执行其他任务,系统负责数据处理和通知。
I/O多路复用 使用select、poll或epoll等机制同时监听多个I/O流的事件,可以同时处理多个I/O操作。 允许单一进程/线程同时管理多个I/O流,适合处理大量并发连接。

2.1 同步阻塞IO(BIO)

假设我们的脑袋就是CPU。

咱在家里,点外卖,念头一瞬之间,肚子也咕咕叫准备好开吃了,但是,外卖送过来是要时间的,怎么办?

嗯,就是笨笨的等,啥也不做。如果我们不忙,似乎是没啥大问题,我就傻等外卖过来呗。

同步阻塞 IO 模型中,应用程序发起 read 调用后,会一直阻塞,直到内核把数据拷贝到用户空间(有数据可用)。

image-20240530221000021

2.2 同步非阻塞 IO(NIO)

现在,外卖还是得等,但是我们傻等啥也不做似乎有点像呆子啊,盯着门口发呆?你手机都不玩?

我们变聪明了,隔五分钟看一次外卖的距离。

同步非阻塞 IO 模型中,应用程序会一直发起 read 调用,每次呢read立即返回个状态值,没ok的话,咱就返回用户空间先做别的事情。

不过这里要注意下,只是准备数据这里咱们不等了。数据从内核空间拷贝到用户空间的这段时间里,线程还是阻塞的,咱得等它拷贝完。

image-20240530221013916

当一个进程循环调用非阻塞式IO等待数据报时,我们称之为“轮询”。

嗯,似乎看起来不呆了,但是你看,咱们又得分心去关注五分钟记得看一次这个事情了,对CPU的消耗相对较大

2.3 信号驱动IO

信号驱动IO解决的是准备数据这里的问题

刚才的问题就是,咱们一直五分钟看一次,很麻烦,费脑子。

有没有办法,外卖到了主动通知我们下啊,我再拿,这样多好。

信号驱动IO,就是加上了这个通知功能,就是下面提到的select和epoll这些系统调用。

咱通过这些系统调用请示下操作系统大哥,准备好数据你再通知我吧。

信号驱动IO是一种通过信号来通知应用程序有IO事件发生的方法。当一个文件描述符准备好进行IO操作时,内核向进程发送一个信号,通知它可以进行相应的IO操作。

应用程序通过signal()sigaction()注册一个信号处理程序来处理特定的信号(通常是SIGIO)。

image-20240530221043274

信号驱动IO,通过减少无效的系统调用,减少了对 CPU 资源的消耗。

2.4 异步IO(AIO)

AIO是为了让准备数据和拷贝数据这两个过程都不阻塞

上面似乎已经很不错了,但是还是有个问题。

通知也通知我了,就是,就是,能不能直接给我送到大门口,而不是小区门口。

AIO的目的,就是如何进一步提升效率,解除后面那步,复制数据(内核态到用户到)的阻塞呢?

AIO即用户线程通过系统调用,告知内核启动某个IO操作,用户线程返回。kernel内核在整个IO操作(包括数据准备、数据复制)完成后,通知用户程序,用户执行后续的业务操作。

image-20240530221055281

在内核的等待数据和复制数据的两个阶段,AIO下的用户线程都不是阻塞的。

2.5 IO多路复用

信号驱动IO主要是用于处理单个文件描述符的IO事件

IO多路复用是用于同时监视和处理多个文件描述符的IO事件,关键字是多个。

多路复用这里常见的系统调用有:

假设一千个1000个socket,其中一个socket有消息来了。

  • select / poll:进程需要遍历所有监视的文件描述符来检查哪些文件描述符有事件发生,效率较低,尤其是在文件描述符数量较多时。

  • epoll:内核直接通知有事件发生的文件描述符,进程只需处理这些就绪的文件描述符,效率较高,特别适合处理大量并发连接。

3.Java中的IO

BIO、AIO、IO多路复用

posted @ 2024-05-30 22:39  羊37  阅读(4)  评论(0编辑  收藏  举报