厘清 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 模型。
版权声明: 本文为 InfoQ 作者【sakila】的原创文章。
原文链接:【http://xie.infoq.cn/article/bb59c6ff35db32c635f4e5d1a】。未经作者许可,禁止转载。
评论