首先来回想一下普通函数调用的机制:程序调用某函数,函数执行,程序等待,函数将 结果和控制权返回给程序,程序继续处理。Reactor 释义“反应堆”,是一种事件驱动机制。 和普通函数调用的不同之处在于:应用程序不是主动的调用某个 API 完成处理,而是恰恰 相反,Reactor 逆置了事件处理流程,应用程序需要提供相应的接口并注册到 Reactor 上, 如果相应的时间发生,Reactor 将主动调用应用程序注册的接口,这些接口又称为“回调函数”。
I/O 多路复用是一个线程里会监视多个文件描述符,在其中一个或者多个描述符准备好之后,内核会通知用户进程,用户进程来处理数据。从内核层面看同步I/O是用户进程将数据从内核空间拷贝到用户空间,这一段时间对于进程来说是阻塞的。异步 I/O 是内核将数据从内核空间拷贝到用户空间,用户进程完全是异步操作 。
Reactor 模式是处理并发 I/O 比较常见的一种模式,用于同步 I/O,中心思想是将所有要 处理的 I/O 事件注册到一个中心 I/O 多路复用器上,同时主线程/进程阻塞在多路复用器上; 一旦有 I/O 事件到来或是准备就绪(文件描述符或 socket 可读、写),多路复用器返回并将事先注册的相应 I/O 事件分发到对应的处理器中。 Reactor 模型有三个重要的组件:
1. 多路复用器:由操作系统提供,在 linux 上一般是 select, poll, epoll 等系统调用。
2. 事件分发器:将多路复用器中返回的就绪事件分到对应的处理函数中。
3. 事件处理器:负责处理特定事件的处理函数。
Reactor 模式是指主线程即 IO 处理单元只负责监听文件描述符上是否有事件发生,有则立刻将该事件通知给工作线程即逻辑单元,除此之外,主线程不做任何其它实质性的动作。读写数据,接受新的连接,以及处理客户请求均在工作线程中完成。
同步 IO 模型实现 Reactor 模式的工作流程:
- 主线程往 epoll 内核事件表中注册 socket 上的读就绪事件。
- 主线程调用
epoll_wait
等待 socket 上有数据可读。 - 当 socket 上有数据可读时,
epoll_wait
通知主线程,主线程则将 socket 可读事件放入请求队列。 - 睡眠在请求队列上的工作线程被唤醒,它从 socket 上读取数据,并处理客户请求,然后往 epoll 内核事件表中注册写就绪事件。
- 主线程调用
epoll_wait
等待 socket 可写。 - 当 socket 可写时,
epoll_wait
通知主线程,主线程将 socket 可写事件放入请求队列中。 - 睡眠在请求队列上的某个工作线程被唤醒,它往 socket 上写入服务器处理客户请求的结果。
参考链接: