标签:
8.1 服务器模型
c/s模型
p2p模型
实际使用的P2P模型通常带有一个专门的发现服务器,提供查找服务
8.2 服务器编程框架
I/O处理单元是服务器管理客户连接的模块
一个逻辑单元通常是一个进程或线程,服务器通常由多个逻辑单元,实现对多个客户任务的并行处理
8.3 I/O模型
socket在创建的时候是默认阻塞的,可以通过给socket系统调用的第二个参数传递SOCK_NONBLOCK标志,或者通过fcntl系统调用的F_SETEL命令,将其设置为非阻塞的
针对阻塞I/O执行的系统调用可能因为无法立即完成而被操作系统挂起,可能被阻塞的系统调用包括accept send recv connect
针对非阻塞I/O执行的系统调用总是立即返回
非阻塞I/O通常要和其他I/O通知机制一起使用,比如I/O复用和SIGIO信号
Linux上最常用的I/O复用函数是select poll epoll_wait
以上都是同步I/O模型,因为I/O读写操作都是在I/O事件发生后由应用程序完成的
异步I/O读写操作总是立即返回,而不论I/O是否阻塞,因为真正的读写操作已经由内核接管
总结:
同步I/O模型要求用户代码自行执行I/O操作,异步I/O机制由内核来执行I/O操作
同步I/O向应用程序通知的是I/O就绪事件,异步I/O向应用程序通知的是I/O完成事件
8.4 两种高效的事件处理模式
服务器通常处理三类事件:I/O事件 信号事件 定时事件
同步I/O模型通常用于实现reactor模式 异步I/O模型通常用于实现proactor模式
reactor模式
同步IO模型(以epoll_wait为例)实现的Reactor模式的工作流程:
1. 主线程往epoll内核事件表中注册socket上的读就绪事件。
2. 主线程调用epoll_wait等待socket上有数据可读。
3. 当socket上有数据可读时,epoll_wait通知主线程。主线程则将socket可读事件放入请求队列。
4. 睡眠在请求队列上的工作线程被唤醒,它从socket读取数据,并处理客户请求,然后往epoll内核事件表中注册该socket上的写就绪事件。
5. 主线程调用epoll_wait等待socket可写。
6. 当socket可写时,epoll_wait通知主线程。主线程将socket可写事件放入请求队列。
7. 睡眠在请求队列上的某个工作线程被唤醒,它往socket上写入服务器处理客户请求的结果。
工作线程从队列中取出事件后,将根据事件是可读或可写执行读写数据和处理请求的操作。因此,在Reactor模式中,没必要区分所谓的“读工作线程”和“写工作线程”。
proactor模式
与Reactor模式不同,Proactor模式将所有IO操作都交给主线程和内核来处理,工作线程仅仅负责业务逻辑。
(以aio_read和aio_write为例)工作流程:
1. 主线程调用aio_read函数向内核注册socket上的读完成事件,并告诉内核用户读缓冲区的位置,以及读操作完成时如何通知应用程序(这里以信号为例,详情sigevent的man手册)
2. 主线程继续处理其他逻辑。
3. 当socket上的数据被读入用户缓冲区后,内核将向应用程序发送一个信号,以通知应用程序数据已经可用。
4. 应用程序预先定义好的信号处理函数选择一个工作线程来处理客户请求。工作线程处理完客户请求之后,调用aio_write函数向内核注册socket上的写完成事件,并告诉内核用户写缓冲区位置,以及写操作完成时如何通知应用程序(仍以信号为例)
5. 主线程继续处理其他逻辑。
6. 当用户缓冲区的数据被写入socket之后,内核将向应用程序发送一个信号,以通知应用程序数据已经发送完毕。
7. 应用程序预先定义好的信号处理函数选择一个工作线程来做善后处理,比如决定是否关闭socket.
模拟proactor模式
主线程执行数据读写,主线程想工作线程通知完成事件
1. 主线程往epoll内核事件表中注册socket上的读就绪事件。
2. 主线程调用epoll_wait等待socket上有数据可读。
3. 当socket上有数据可读时,epoll_wait通知主线程。主线程从socket循环读取数据,知道没有更多数据可读,然后将读取到的数据封装成一个请求对象并插入到请求队列。
4. 睡眠在请求队列上的某个工作线程被唤醒,它获得请求对象并处理客户请求,然后网epoll内核事件表中注册socket上的写就绪事件。
5. 主线程调用epoll_wait等待socket可写。
6.0当socket可写时,epoll_wait通知主线程。主线程网往socket上写入服务器处理客户端请求的结果。
8.5 两种高效的并发模式
并发编程的目的是让程序同时执行多个任务,适合I/O密集型,不适合计算密集型
半同步半异步模式
这里的“同步”和“异步”和前面的IO的“同步”“异步”是完全不同的概念。在IO模型中,“同步”和“异步”区分的是内核向应用程序通知的是何种IO事件(是就绪事件还是完成事件),以及该由谁来完成IO读写(是应用程序还是内核)。
在并发模式中,“同步”指的是程序完全按照代码序列的顺序执行;“异步”指的是程序的执行需要由系统事件来驱动。常见的系统事件包括中断、信号等。
显然异步线程的执行效率高,实时性强,是很多嵌入式系统采用的模型。但编写异步方式执行的程序相对复杂,难于调试和扩展,而且不适合于大量的并发。而同步线程则相反,它虽然效率相对较低,实时性较差,但逻辑简单。
在半同步半异步模式中,同步线程用于处理客户逻辑,异步线程用于处理IO事件。异步线程监听到客户请求后,就将其封装成请求对象并插入到请求队列中。请求队列将通知某个工作在同步模式的工作线程来读取并处理该请求对象。
缺点:
1. 主线程和工作线程on共享请求队列,主线程往请求队列添加任务和工作线程从请求队列取出任务都需要给队列加锁
2. 每个工作线程同一时间只能处理一个客户请求,工作线程的切换将耗费大量CPU时间
一种高效的半同步/半异步模式(nginx应该是这种模式)
它每个工作线程能同时处理多个客户连接,主线程只管监听socket,连接socket由工作线程来管理
标签:
原文地址:http://www.cnblogs.com/raichen/p/5032745.html