深入理解同步和异步与reactor和proactor模式
在现代网络编程中,I/O 设计模式对于提高性能和资源利用率至关重要。本文将探讨两种主要的网络 I/O 设计模式:同步 I/O 和异步 I/O,以及它们的实现方式。
同步 I/O
同步 I/O 模式要求用户通过系统调用函数,如 read()
, write()
, connect()
等,将网络数据从内核态拷贝到用户态的缓冲区,或者从用户态拷贝到网卡缓冲区。这个过程涉及到用户态和内核态之间的数据拷贝,可能会占用较多的 CPU 时间。同步调用的特点是在数据传输过程中,用户线程会被阻塞,直到数据传输完成。
异步 I/O
与同步 I/O 不同,异步 I/O 允许用户线程直接使用内核态的数据,无需手动读取。这意味着数据在内核态与用户态的传输已经由内核完成,用户态可以直接对数据进行业务处理。
Reactor 模式
Reactor 模式是一种常见的同步I/O 模式,通常与多路复用技术(如 epoll)结合使用。其工作流程大致如下:
- 初始化 epoll 对象和监听 socket。
- 将 socket 文件描述符(fd)注册到 epoll 对象,并调用多路复用函数
epoll_wait()
。 - 根据
epoll_wait()
返回的事件类型,由工作线程进行 I/O 操作。
Proactor 模式
Proactor 模式是异步 I/O 的一种实现方式,它与 Reactor 模式的主要区别在于它是异步的。在 Proactor 模式中,用户不需要关心读写就绪事件,只需要关注读写完成事件。数据在内核态与用户态的传输已经由内核完成,业务进程只需要在用户态处理结果即可。
一个典型的 Proactor 模式实现是 Boost ASIO 库,它提供众多异步函数,比如 async_read
,允许用户指定缓冲区和回调函数,当内核完成数据接收后,会自动调用回调函数。
Linux 与 Windows 的比较
在 Linux 下,目前没有系统级别的接口直接支持 Proactor 模式,而 Windows 的 IOCP(I/O 完成端口)提供了这种支持。因此,在 Windows 上实现 Proactor 模式的程序效率可能会略高于 Linux。在 Linux 上,Proactor 模式的实现通常是模拟 IOCP 的过程。
Proactor 模式的实现流程
下图是asio官方给的Proactor 实现结构图
涉及以下几个关键组件:
- Asynchronous Operation(异步操作):类似于 read、write 等系统调用。
- Asynchronous Operation Processor(异步操作处理器):监听读写就绪事件并执行异步操作,然后将读写完成事件的通知插入到完成事件队列。
- Completion Event Queue(完成事件队列):存放完成事件。
- Asynchronous Event Demultiplexer(异步事件多路分离器):监听完成事件队列,如果有完成事件,则调用完成事件处理器。
- Completion Handler(完成事件处理器):用户的回调函数。
总结来说,ASIO 的 Proactor 模式实现基本上是在 Reactor 模式的基础上,将 I/O 读写操作封装起来,在内部完成,并通过新增的完成事件队列触发用户绑定的回调函数。