事件驱动模型
同步和异步
在了解 阻塞 和 非阻塞 的概念之前,需要先理解同步和异步
高人见解,直接明了:
---------------------------------------------------------------------------------------------------------------------------------------
同步是指:当程序1调用程序2时,程序1停下不动,直到程序2完成回到程序1来,程序1才继续执行下去。
异步是指:当程序1调用程序2时,程序1径自继续自己的下一个动作,不受程序2的的影响。
---------------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------------------
同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。
异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。
---------------------------------------------------------------------------------------------------------------------------------------
同步:所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。
异步:当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。
阻塞和非阻塞
阻塞式I/O模型:默认情况下,所有套接字都是阻塞的。怎么理解?先理解这么个流程,一个输入操作通常包括两个不同阶段:(1)等待数据准备好;(2)从内核向进程复制数据。对于一个套接字上的输入操作,第一步通常涉及等待数据从网络中到达。当所有等待分组到达时,它被复制到内核中的某个缓冲区。第二步就是把数据从内核缓冲区复制到应用程序缓冲区。阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。
非阻塞式I/O模型: 进程把一个套接字设置成非阻塞是在通知内核,当所请求的I/O操作非得把本进程投入睡眠才能完成时,不要把进程投入睡眠,而是返回一个错误。看看非阻塞的套接字的 recvfrom 操作如何进行,可以看出 recvfrom 总是立即返回。非阻塞调用指即使不能立刻得到结果,该函数也不会阻塞当前线程,而会立刻返回。
事件驱动模型的概念
事件驱动模型:鼠标的一个点击,移动,键盘的按键按下等等操作,都是对应操作系统的一个事件,然后应用程序接受你的操作进行处理
基本事件驱动模型(服务器处理模型)
通常,我们写服务器处理模型的程序时,有以下几种模型:
(1)每收到一个请求,创建一个新的进程,来处理该请求;
(2)每收到一个请求,创建一个新的线程,来处理该请求;
(3)每收到一个请求,放入一个事件列表,让主进程通过非阻塞I/O方式来处理请求
上面的几种方式,各有千秋,
第(1)种方法,由于创建新的进程的开销比较大,所以,会导致服务器性能比较差,但实现比较简单。
第(2)种方式,由于要涉及到线程的同步,有可能会面临死锁等问题。
第(3)种方式,在写应用程序代码时,逻辑比前面两种都复杂。
综合考虑各方面因素,一般普遍认为第(3)种方式是大多数网络服务器采用的方式,这也是本文讨论的重点—事件驱动处理库。
select 事件驱动模型
使用 select 事件驱动模型,会创建所关注的事件的描述符集合(fd_set),对于一个描述符(或称为句柄,不管是socket句柄,还是其他文件或命名管道或设备句柄),可以关注其上面的读(read)、写(write)、异常(exception)事件,所以通常,要创建三个fd_set, 一个用来收集关注读事件的描述符,一个用来收集关注写事件的描述符,另外一个用来收集关注异常事件的描述符。
轮询所有中的每一个fd ,检查是否有相应的事件发生,如果有,就进行处理
poll 事件驱动模型
在这里要说明的是,poll与select的主要区别在与,select需要为读、写、异常事件分别创建一个描述符集合,最后轮询的时候,需要分别轮询这三个集合。而poll只需要一个集合,在每个描述符对应的结构上分别设置读、写、异常事件,最后轮询的时候,可以同时检查三种事件。
epoll 事件驱动模型
epoll是和上面的poll和select不同的一个事件驱动库,上面的poll和select库,它们的最大的问题就在于效率。它们的处理方式都是创建一个事件列表,然后把这个列表发给内核,返回的时候,再去轮询检查这个列表,这样在描述符比较多的应用中,效率就显得比较低下了。一种比较好的做法是,把描述符列表交给内核,一旦有事件发生,内核把发生事件的描述符列表通知给进程,这样就避免了轮询整个描述符列表。epoll就是这样一种模型。