同步I/O模型要求用户代码自行执行I/O操作(将数据从内核缓冲区读入用户缓冲区,或将数据从用户缓冲区写入内核缓冲区),而异步I/O则由内核来执行I/O操作。
同步I/O向应用程序通知的是I/O就绪事件,而异步I/O向应用程序通知的是I/O完成时间。
1. Reactor模式
工作流程:
- 主线程网epoll内核事件表中注册socket上的读就绪时间
- 主线程调用epoll_wait等待socket上有数据可读
- 当socket上有数据可读时,epoll_wait通知主线程。主线程则将socket可读事件放入请求队列
- 睡眠在请求队列的某个工作线程被唤醒,它从socket读取数据,并处理客户请求,然后往epoll内核事件表中注册该socket上的写就绪时间
- 主线程调用epoll_wait等待socket可写
- 当socket可写时,epoll_wait通知主线程。主线程将socket可写事件放入请求队列
- 睡眠在请求队列上的某个工作线程被唤醒,它往socket上写入服务器处理客户请求的结果
2. Proactor模式
工作流程:
- 主线程调用aio_read函数向内核注册socket上的读完成事件,并告诉内核用户读缓冲区的位置,以及读操作完成时如何通知应用程序(信号为例)
- 主线程继续处理其他逻辑
- 当socket上的数据被读入用户缓冲区后,内核将向应用程序发送一个信号,以通知应用程序数据已经可用
- 应用程序预先定义好的信号处理程序选择一个工作线程来处理客户请求。工作线程处理完客户请求之后,调用aio_write函数向内核注册socket上的写完成时间,并告诉内核用户写缓冲区的位置,以及写操作完成时如何通知应用程序(信号)
- 主线程继续处理其他逻辑
- 当用户缓冲区的数据被写入socket之后,内核将向应用程序发送一个信号,以通知应用程序数据已经发送完毕
- 应用程序预先定义好的信号处理函数选择一个工作线程来做善后处理
3. 模拟Proactor模式
使用同步I/O方法可以模拟Proactor模式:主线程执行数据读写操作,读写完成之后,主线程向工作线程通知这一“完成事件”,那么从工作线程的角度看,它们就直接获得了数据读写的结果,只需要对读写的结果进行逻辑处理。
4. 半同步/半异步模式
这里的同步异步不是I/O模型的,而是并发模式的:
同步指的是程序完全按照代码序列的顺序执行,异步指的是程序的执行需要由系统事件来驱动、
半同步/半异步模式中,同步线程用于处理客户逻辑,异步线程用于处理I/O事件(主线程,负责分发任务)。
结合考虑这两种事件处理模式和几种I/O模型,存在多种变体:
半同步半反应堆模式
半同步半反应堆模式.jpg异步线程:主线程
事件处理模式:Reactor,请求队列的任务时就绪的连接socket,工作线程自己从socket上读取客户请求和写入应答
缺点:
- 主线程和工作线程共享请求队列,需要加锁保护,耗费时间
- 每个工作线程同一时间只能处理一个客户请求,容易堆积任务
半同步/半异步模式
半同步半异步模式.jpg每个工作线程都能同时处理多个客户连接
主线程只管理监听socket,连接socket由工作线程管理,主线程通过管道向工作线程分发任务
每个线程(主线程和工作线程)都维持自己的时间循环,各自独立地监听不同的时间。
领导者/追随者模式
多个工作线程轮流获得事件源集合,轮流监听、分发并处理事件。
提高性能
- 资源池:避免频繁分配和销毁资源开销
- 数据复制:避免不必要的文件复制,共享内存,传递指针
- 上下文切换和锁:避免频繁切换,减小锁的粒度,读写锁等。
网友评论