五种网络IO模型
基础
在linux系统中,对于一次读取IO请求的操作,数据并不会直接拷贝到用户程序的用户空间缓冲区。它首先会被拷贝到操作系统的内核空间,然后才会从操作系统内核的缓冲区拷贝到用户空间的缓冲区。
在进行一次等待请求IO的操作中分为两部分
- 等待数据到达内核缓冲区
- 将内核空间的数据拷贝到用户空间
网络IO的本质是Socket的读取,Socket在linux中被当成流。所以网络IO也是对流的读取。我们接下来以read()方法来举例。
1,同步阻塞IO模型
我们进程调用一个read()方法,会经历两种状态:
- 等待网络数据到达(网络数据到达需要一段时间)
- 从内核空间拷贝到用户空间
在用户应用程序等待网络数据到达内核缓冲区这段时间是进入阻塞状态,什么都不干,就等数据准备好。等到数据到达了内核缓冲区后,用户应用程序 继续阻塞,等待内核缓冲区将数据拷贝到用户空间。在等待数据和复制数据这两个阶段进程都将处于阻塞状态。这严重影响了性能。
2,同步非阻塞IO模型
等待数据:非阻塞就是说在调用read()后,如果网络请求数据并没有到达内核空间,用户进程并不会被阻塞,而是会给用户进程返回一个error。进程收到这个error之后,进程会去干别的事。但是它会隔一段时间就去发送一个这样的请求,看数据是否准备好,如果还没好就会这样轮询 的去检查内核数据,直到数据准备好。
拷贝数据:从内核空间拷贝到用户空间,这一步用户进程还是会阻塞。
所以说阻塞/非阻塞指的是用户空间和内核空间之间。
3,IO多路复用
同步非阻塞方式虽然进程在等待数据到达内核空间时不用阻塞住了,但是它得不停的去检查内核数据啊。不停地轮询都是要靠CPU来控制,那是不是就要花费大量的CPU时间。“后台”一般都是由多个任务在同时进行的,那么为什么不让一个进程去帮忙循环查询这些任务的完成状态,只要有任何一个任务完成,就去处理它。这就是IO多路复用
系统提供了select poll epoll三种方式去实现IO多路复用。
当用户线程调用了select时,用户进程进入阻塞状态,同时,kernel会“监视”所有select负责的socket,当任何一个socket里的数据准备好了,select将返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。
IO多路复用技术在单线程下可能性能还不如阻塞IO,但是在处理多个客户端接入请求时,select可以同时监听对个
在I客户端请求,使效率提高.
4,信号IO模型
Socket进行信号驱动IO,并安装一个信号处理函数,进程继续运行自己的事情不会阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。然后将数据拷贝到用户空间。(第二段时间是阻塞的)
5,异步非阻塞IO
用户调用read后,无论数据是否到达,都会直接返回给用户进程,然后用户 进程干自己的其它事,不会阻塞。等到socket数据准备好了,然后内核将数据直接拷贝到用户空间,然后向用户空间发送通知。这两个等待数据,拷贝数据是非阻塞的。