前言
两种I/O多路复用模式:Reactor和Proactor。
一般地,I/O多路复用机制都依赖于一个事件多路分离器(Event Demultiplexer)。分离器对象可将来自事件源的I/O事件分离出来,并分发到对应的read/write事件处理器(Event Handler)。开发人员预先注册需要处理的事件及其事件处理器(或回调函数);事件分离器负责将请求事件传递给事件处理器。两个与事件分离器有关的模式是Reactor和Proactor。Reactor模式采用同步IO,而Proactor采用异步IO。
一般地,I/O多路复用机制都依赖于一个事件多路分离器(Event Demultiplexer)。分离器对象可将来自事件源的I/O事件分离出来,并分发到对应的read/write事件处理器(Event Handler)。开发人员预先注册需要处理的事件及其事件处理器(或回调函数);事件分离器负责将请求事件传递给事件处理器。两个与事件分离器有关的模式是Reactor和Proactor。Reactor模式采用同步IO,而Proactor采用异步IO。
在Reactor中,事件分离器负责等待文件描述符或socket为读写操作准备就绪,然后将就绪事件传递给对应的处理器,最后由处理器负责完成实际的读写工作。
在Proactor模式中,处理器--或者兼任处理器的事件分离器,只负责发起异步读写操作。IO操作本身由操作系统来完成。传递给操作系统的参数需要包括用户定义的数据缓冲区地址和数据大小,操作系统才能从中得到写出操作所需数据,或写入从socket读到的数据。事件分离器捕获IO操作完成事件,然后将事件传递给对应处理器。比如,在windows上,处理器发起一个异步IO操作,再由事件分离器等待IOCompletion事件。典型的异步模式实现,都建立在操作系统支持异步API的基础之上,我们将这种实现称为“系统级”异步或“真”异步,因为应用程序完全依赖操作系统执行真正的IO工作。
在Proactor模式中,处理器--或者兼任处理器的事件分离器,只负责发起异步读写操作。IO操作本身由操作系统来完成。传递给操作系统的参数需要包括用户定义的数据缓冲区地址和数据大小,操作系统才能从中得到写出操作所需数据,或写入从socket读到的数据。事件分离器捕获IO操作完成事件,然后将事件传递给对应处理器。比如,在windows上,处理器发起一个异步IO操作,再由事件分离器等待IOCompletion事件。典型的异步模式实现,都建立在操作系统支持异步API的基础之上,我们将这种实现称为“系统级”异步或“真”异步,因为应用程序完全依赖操作系统执行真正的IO工作。
Reactor-同步IO实现
Reactor模式:主线程(IO处理单元)只负责监听socket文件描述符上是否有事件发生,有的话就立即将该事件通知工作线程(逻辑处理单元)。除此之外,主线程不做任何其他实质性的工作,读写数据、接受新的连接请求、处理客户端请求均在工作线程中完成。
以epoo_wait()为例实现的Reactor模式的工作流程为(所有IO复用技术都是基于同步IO模型):
(1)主线程往epoll内核事件表中注册socket的可读事件(就绪读),进而调用epoll_wait()监听socket上的可读事件。
(2)当socket上有数据可读时(含有客户端来连接),epoll_wait()通知主线程,主线程将socket的可读事件放入请求队列。
(3)唤醒在请求队列上的某个工作线程(请求队列上有多个工作线程,空闲时是休眠状态),工作线程执行相应操作后往该socket上写入请求的处理结果。
以epoo_wait()为例实现的Reactor模式的工作流程为(所有IO复用技术都是基于同步IO模型):
(1)主线程往epoll内核事件表中注册socket的可读事件(就绪读),进而调用epoll_wait()监听socket上的可读事件。
(2)当socket上有数据可读时(含有客户端来连接),epoll_wait()通知主线程,主线程将socket的可读事件放入请求队列。
(3)唤醒在请求队列上的某个工作线程(请求队列上有多个工作线程,空闲时是休眠状态),工作线程执行相应操作后往该socket上写入请求的处理结果。
主线程中的epoll_wait()只是用于检测监听socket上的连接而不能用来检测连接socket上的读写事件,因为读写事件是主进程和工作线程通过aio_read()和aio_write()来向内核注册的,内核负责监听读写事件且执行读写操作,读写完毕后再通过信号机制向应用程序报告,应用程序的信号处理函数完成后续操作。
Proactor-异步IO实现
Proactor模式是将所有IO事件操作都交由主线程和内核处理,工作线程只负责业务逻辑。
使用异步IO模式实现的Proactor模式的工作流程:(以aio_read()和aio_write()为例)
(1)主线程调用aio_read()函数向内核注册sockst上的读完成事件,并告诉内核在用户空间的读缓冲区的位置,以及读操作完成时如何通知应用程序(比如信号机制)。
(2)主线程继续处理业务逻辑。
(3)当socket上的数据被读入用户缓冲区后(读操作交由内核完成),内核将向应用程序发送信号以通知应用程序数据可用。
(4)应用程序在预先定义的信号处理函数中选择一个工作线程来处理客户需求。工作线程处理完客户请求后调用aio_write()函数向内核注册socket上的写完成事件,并告诉内核用户写缓冲区的位置以及写操作完成时如何通知应用程序,即写操作交由内核执行。
(5)主线程继续处理其他业务逻辑。
(6)当用户缓冲区的数据被内核写入socket后内核向应用程序发送一个信号以通知应用程序发送完毕。
(7)应用程序预先定义好的信号处理函数选择一个工作线程来做善后处理,如决定是否关闭socket连接。
使用异步IO模式实现的Proactor模式的工作流程:(以aio_read()和aio_write()为例)
(1)主线程调用aio_read()函数向内核注册sockst上的读完成事件,并告诉内核在用户空间的读缓冲区的位置,以及读操作完成时如何通知应用程序(比如信号机制)。
(2)主线程继续处理业务逻辑。
(3)当socket上的数据被读入用户缓冲区后(读操作交由内核完成),内核将向应用程序发送信号以通知应用程序数据可用。
(4)应用程序在预先定义的信号处理函数中选择一个工作线程来处理客户需求。工作线程处理完客户请求后调用aio_write()函数向内核注册socket上的写完成事件,并告诉内核用户写缓冲区的位置以及写操作完成时如何通知应用程序,即写操作交由内核执行。
(5)主线程继续处理其他业务逻辑。
(6)当用户缓冲区的数据被内核写入socket后内核向应用程序发送一个信号以通知应用程序发送完毕。
(7)应用程序预先定义好的信号处理函数选择一个工作线程来做善后处理,如决定是否关闭socket连接。
主线程中的epoll_wait()只是用于检测监听socket上的连接而不能用来检测连接socket上的读写事件,因为读写事件是主进程和工作线程通过aio_read()和aio_write()来向内核注册的,内核负责监听读写事件且执行读写操作,读写完毕后再通过信号机制向应用程序报告,应用程序的信号处理函数完成后续操作。
Proactor-同步IO模拟
Proactor模式的读写socket操作是交由内核处理,即异步IO模式,但是编程使用的IO复用技术是同步IO,我们可以利用同步IO方式模拟出Proactor模式,其原理为:主线程执行数据的读写操作(和监听listen_socket),读写完成后主线程向工作通知这一”完成事件”,那么从工作线程的角度看,和异步IO实现的Proactor模式的工作线程一样,它们直接获得了数据的读写结果,接下来要做的只是对读写结果进行逻辑处理。
以epoll_wait()为例,使用同步IO模型模拟Proactor模式的工作流程:
(1)主线程往epoll内核事件表中注册socket上的读就绪事件,并调用epoll_wait()等待socket上有数据可读(有客户端连接也是可读)
(2)当socket上有数据可读时epoll_wait()将通知主线程,主线程从socket循环读取数据直到没有更多数据可读,然后将读取到的数据封装成一个请求对象并插入请求队列。主线程调用epoll_wait()等待socket可写,以返回数据给该socket。
(3)请求队列上某个睡眠的工作线程被唤醒,它获得请求对象并处理该请求,然后往epoll内核事件表中注册socket上的写就绪事件(目的是要返回数据给该socket)。
(4)当socket可写时,epoll_wait()通知主线程,主线程往socket上写入服务端处理客户端的请求结果,即工作线程的数据。
以epoll_wait()为例,使用同步IO模型模拟Proactor模式的工作流程:
(1)主线程往epoll内核事件表中注册socket上的读就绪事件,并调用epoll_wait()等待socket上有数据可读(有客户端连接也是可读)
(2)当socket上有数据可读时epoll_wait()将通知主线程,主线程从socket循环读取数据直到没有更多数据可读,然后将读取到的数据封装成一个请求对象并插入请求队列。主线程调用epoll_wait()等待socket可写,以返回数据给该socket。
(3)请求队列上某个睡眠的工作线程被唤醒,它获得请求对象并处理该请求,然后往epoll内核事件表中注册socket上的写就绪事件(目的是要返回数据给该socket)。
(4)当socket可写时,epoll_wait()通知主线程,主线程往socket上写入服务端处理客户端的请求结果,即工作线程的数据。
参考:http://blog.csdn.net/qq_29344757/article/details/79054623