Linux两种处理模式reactor模式proactor模式( 二 )

二、Proactor模式

Proactor模式特点与Reactor不同,Proactor模式将所有的I/O操作都交给主线程和内核来处理,工作线程仅仅负责业务逻辑
Proactor模式的工作流程①主线程调用aio_read函数向内核注册socket上读完成事件,并告诉内核用户读缓冲区的位置,以及读操作完成时如何通知应用程序(这里以信号为例)②主线程继续处理其他逻辑③当socket上的数据被读入用户缓冲区后,内核将向应用程序发送一个信号,以通知应用程序数据已经可用④应用程序预先定义好的信号处理函数选择一个工作线程来处理客户请求 。工作线程处理完客户请求之后,调用aio_write函数向内核注册socket上的写完成事件,并告诉内核用户写缓冲区的位置,以及写操作完成时如何通知应用程序(这里以信号为例)⑤主线程继续处理其他逻辑⑥当用户缓冲区的数据被写入socket之后,内核将向应用程序发送一个信号,以通知应用程序数据已经发送完毕⑦应用程序预先定义好的信号处理函数选择一个工作线程来做善后处理,比如决定是否关闭socket在上图中,连接socket上的读写事件是通过aio_read/aio_write向内核注册的,因此内核将通过信号来向应用程序报告连接socket上的读写事件 。所以,主线程的epoll_wait调用仅能用来检测监听socket上的连接请求事件,而不能用来检测连接socket的读写事件
三、使用同步I/O模拟Proactor模式
原理:主线程执行数据读写操作,读写完成之后,主线程向工作线程通知这一“完成事件” 。那么从工作线程的角度来看,它们就直接获得了数据读写的结果,接下来要做的只是对读写的结果进行逻辑处理
工作流程:①主线程往epoll内核事件表中注册socket上的读就绪事件②主线程调用epoll_wait等待socket上有数据可读③当socket上有数据可读时,epoll_wait通知主线程 。主线程从socket循环读取数据,直到没有更多数据可读,然后将读取到的数据封装成一个请求对象并插入请求队列④睡眠在请求队列上的某个工作线程被唤醒,它获得请求对象并处理客户请求,然后往epoll内核事件表中注册socket上的写就绪事件⑤主线程调用epoll_wait等到socket可写⑥当socket可写时,epoll_wait通知主线程 。主线程往socket上写入服务器处理客户请求的结果
四、几种开源库
  • 下面是几种使用到上面技术的开源库:libevent:名气最大,应用最广泛,历史悠久的跨平台事件库libev:较 libevent 而言,设计更简练,性能更好,但对 windows 支持不够好;libuv:开发 node 的过程中需要一个跨平台的事件库,他们首选了 libev,但又要支持 Windows,故重新封装了一套,linux 下用 libev 实现,Windows 下用 IOCP 实现
优先级libevent:激活的事件组织在优先级队列中,各类事件默认的优先级是相同的,可以通过设置 事件的优先级使其优先被处理libev:也是通过优先级队列来管理激活的时间,也可以设置事件优先级libuv:也是通过优先级队列来管理激活的时间,也可以设置事件优先级
事件循环libevent:event_base 用于管理事件libev:激活的事件组织在优先级队列中,各类事件默认的优先级是相同的,libuv:可以通 过设置事件的优先级 使其优先被处理
线程安全event_base 和 loop 都不是线程安全的,一个 event_base 或 loop 实例只能在用户的一个线程 内访问(一般是主线程),注册到 event_base 或者 loop 的 event 都是串行访问的,即每个执 行过程中,会按照优先级顺序访问已经激活的事件,执行其回调函数 。所以在仅使用一个 event_base 或 loop 的情况下,回调函数的执行不存在并行关系




推荐阅读