参考链接
I/O多路复用
IO多路复用之select、poll、epoll详解
epoll讲解
深入Go语言网络库的基础实现
I/O模型
I/O模型
1.1 阻塞I/O模型
- 最常用的I/O模型,默认情况下,所有文件操作都是阻塞的。
- 比如I/O模型下的套接字接口:在进程空间中调用recvfrom,其系统调用直到数据包到达且被复制到应用进程的缓冲区中或者发生错误时才返回,在此期间一直等待。
- 进程在调用recvfrom开始到它返回的整段时间内都是被阻塞的,所以叫阻塞I/O模型。
1.2 非阻塞I/O模型
recvfrom从应用层到内核的时候,就直接返回一个EWOULDBLOCK错误,一般都对非阻塞I/O模型进行轮询检查这个状态,看内核是不是有数据到来
1.3 I/O复用模型
- Linux提供select/poll,进程通过将一个或多个fd传递给select或poll系统调用,阻塞在select操作上,这样,select/poll可以帮我们侦测多个fd是否处于就绪状态。
- select/poll是顺序扫描fd是否就绪,而且支持的fd数量有限,因此它的使用受到了一些制约。
- Linux还提供一个epoll系统调用,epoll使用基于事件驱动方式代替顺序扫描,因此性能更高。当有fd就绪时,立即回调函数rollback。
1.4 信号驱动I/O模型
首先开启套接口信号驱动I/O功能,并通过系统调用sigaction执行一个信号处理函数(此系统调用立即返回,进程继续工作,非阻塞)。当数据准备就绪时,就为改进程生成一个SIGIO信号,通过信号回调通知应用程序调用recvfrom来读取数据,并通知主循环函数处理数据。
1.5 异步I/O
- 告知内核启动某个操作,并让内核在整个操作完成后(包括数据的复制)通知进程。
- 信号驱动I/O模型通知的是何时可以开始一个I/O操作,异步I/O模型有内核通知I/O操作何时已经完成。
I/O模型区别
我相信很多人看过网上一篇关于epoll的讲解,在这里我用里面的举例来说。假如现在我要等快递员给我派送快递,那么各种I/O模型对应实际就是:
- 阻塞I/O:我在家里一直等快递员电话,电话没来的时候我就睡觉,电话来了我就醒过来拿快递
- 非阻塞I/O:我就一直醒着,而且我一直打电话给快递员:我的快递来了没?。就这样一直循环着
- I/O多路复用:我用手机下载了一个快递助手,然后就去睡觉了。当有快递来的时候,快递助手就会响铃叫醒我,然后我再看是哪个快递就行了。
信号驱动和异步驱动的区别
- 信号驱动IO是指:进程预先告知内核,使得 当某个socketfd有events(事件)发生时,内核使用信号通知相关进程。
- 异步IO(Asynchronous IO)是指:进程执行IO系统调用(read / write)告知内核启动某个IO操作,内核启动IO操作后立即返回到进程。IO操作即内核当中的服务例程。
异步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能用的消息并执行接下来的操作。
- 异步I/O模型
- 内核:等待这个I/O有消息了,接受到数据。
- 进程:从缓存中得到数据。
信号驱动的作用在于在等待I/O可用的过程中可以执行其它的指令,这种方法从理论上看 倒是不错。由于进程已经休眠,就不会再占用CPU,仅当I/O可用时它才恢复执行。但是这种方法的问题在于信号处理的开销有点大。若只是少数的请求还没有问题,若是每分钟收到100个请求,那就几乎一直都在捕获信号。每秒钟捕获上百个信号的开销是相当大的,不单是进程,对于内核发送信号的开销而言也是一样的。
因此,在高性能的服务器编程中,用异步I/O来处理多个I/O更为高效。当然,也可以用I/O复用模型来实现(epoll是一个相当高效的方法),但是对于Regular File来说,是不能使用epoll的,因为不能设置非阻塞模式(O_NOBLOCK 方式对于传统文件句柄是无效的),这个时候,异步I/O是一个很不错的选择
I/O多路复用
- I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
- 与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。
使用场景
IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合:
1)当客户处理多个描述符时(一般是交互式输入和网络套接口),必须使用I/O复用。
2)当一个客户同时处理多个套接口时,这种情况是可能的,但很少出现。
3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。
4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。
5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。