写点什么

厘清 I/O 模型

用户头像
sakila
关注
发布于: 2021 年 02 月 25 日
厘清 I/O 模型

同步与异步(使用资源阶段)

首先来解释同步和异步的概念,这两个概念与消息的通知机制有关。也就是同步与异步主要是从消息通知机制角度来说的。

概念描述

所谓同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列。要么成功都成功,失败都失败,两个任务的状态可以保持一致。

所谓异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列。

阻塞与非阻塞(等待资源阶段)

阻塞和非阻塞这两个概念与程序(线程)等待消息通知(无所谓同步或者异步)时的状态有关。也就是说阻塞与非阻塞主要是程序(线程)等待消息通知时的状态角度来说的。

概念描述

阻塞调用是指调用结果返回之前,当前线程会被挂起,一直处于等待消息通知,不能够执行其他业务。函数只有在得到结果之后才会返回。

小结

同步与异步是调用方法外部的概念,等待返回结果就是同步,不等待就是异步。

阻塞与非阻塞是调用方法内部的概念,当前线程没有执行其他操作就是阻塞,执行其他的就是非阻塞。

I/O 模型

操作系统将寻址空间(虚拟存储空间)分为两部分,内核空间和用户空间。

I/O 多路复用:同步阻塞,可以监听多个 socket 状态


服务器通信过程分析


这个过程中,存在两种类型操作:

  • CPU 计算/业务处理(CPU 密集型)

  • IO 操作与等待/网络、磁盘、数据库(IO 密集型)

用户态与内核态


用户空间:应用程序

内核空间:操作系统内部空间

五种 IO 模型


阻塞式 IO(Blocking,BIO)


建立连接后,此时不能再接收其他客户端的连接请求,只能等待当前操作执行完成。

可以通过多线程来支持多个客户端的连接。

小明去咖啡店买咖啡,小明在柜台等着(同步),服务员守着咖啡,煮好了才送上来(阻塞)。

比较节省 CPU 资源,但是线程休眠会有上下文切换。

同步非阻塞式 IO(Non-Blocking IO,NIO)


和阻塞 IO 类比,内核会立即返回,返回后获得足够的 CPU 时间继续做其它的事情。

用户进程第一个阶段不是阻塞的,需要不断的主动询问 kernel 数据好了没有;第二个阶段依然总是阻塞的。

小明在柜台等着(同步),服务员不断去看咖啡好没好,煮好了就送上来(非阻塞)。

多路复用 IO(Multiplexing IO),或事件驱动 IO(event-driven IO)


在单个线程里同时监控多个套接字,通过 select 或 poll 轮询所负责的所有 socket,当某个 socket 有数据到达了,就通知用户进程。

进程先是阻塞在 select/poll 上,再是阻塞在读操作的第二个阶段上。

与 BIO 不同的是,内核使用 select 替代请求进程该做的轮询操作。

小明在柜台等着(同步),服务员让后厨不断去看咖啡好没好,服务员不能接待其他人。轮询哪一壶咖啡煮好了(多路复用),后厨通知服务员,服务员送上来。

好处是可以在同一条阻塞线程上处理多个不同端口的监听。

  • select 就是轮询,在 Linux 上限制个数一般为 1024 个

  • poll 解决了 select 的个数限制,但是依然是轮询

  • epoll 解决了个数的限制,同时解决了轮询的方式

select/poll 的几大缺点: (1)每次调用 select,都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大 (2)同时每次调用 select 都需要在内核遍历传递进来的所有 fd,这个开销在 fd 很多时也很大 (3)select 支持的文件描述符数量太小了,默认是 1024

epoll(Linux 2.5.44 内核中引入,2.6 内核正式引入,可被用于代替 POSIX select 和 poll 系统调用): (1)内核与用户空间共享一块内存 (2)通过回调解决遍历问题 (3)fd 没有限制,可以支撑 10 万连接

信号驱动 IO(Signal-Driven IO)


信号驱动 IO 与 BIO 和 NIO 最大的区别就在于,在 IO 执行的数据准备阶段,不会阻塞用户进程(此时用户进程处理其他事情)。

信号驱动 IO 收到的通知时可以进行复制操作(阶段 2),异步 IO 收到的通知是复制操作已完成。

小明在柜台等着(同步),服务员让后厨不断去看咖啡好没好,服务员去接待其他客人,后厨通知服务员咖啡煮好了(发送信号),服务员送上来(非阻塞)。

可以去了解一下 线程池→EDA→SEDA(Staged Event-Driven Architecture)

异步非阻塞式 IO(Asynchronous IO,AIO)


用户进程发出系统调用后立即返回,内核等待数据准备完成,然后将数据拷贝到用户进程缓冲区,然后发送信号告诉用户进程 IO 操作执行完毕(与 SIGIO 相比,一个是发送信号告诉用户进程数据准备完毕,一个是 IO 执行完毕)。

小明点完单在座位上玩(异步),服务员让后厨不断去看咖啡好没好,服务员去接待其他客人,后厨通知服务员咖啡煮好了(发送信号),服务员送上来(非阻塞)。

Windows 下的是 IOCP 模型。


发布于: 2021 年 02 月 25 日阅读数: 22
用户头像

sakila

关注

守正出奇 2019.04.24 加入

Developer/Designer/Foodie

评论

发布
暂无评论
厘清 I/O 模型