IO模型
同步和异步
同步
函数或方法被嗲用的时候,调用者是否得到最终结果
直接得到最终结果的,就是同步调用,
不直接得到最终结果的,就是异步调用
阻塞和非阻塞
函数与或方法调用的时候,是否立刻返回
立刻返回就是非阻塞调用
不立刻返回就是阻塞调用
注意:
同步,异步强调的是,是否得到(最终的结果)
阻塞,非阻塞敲掉的是时间,是否等待
IO模型
IO的两个阶段
- 数据准备阶段
- 内核空间赋值回用户进程缓冲区阶段(将数据从内核拷贝到进程中)]
同步IO包括:阻塞IO,非阻塞IO,IO多路复用
阻塞IO(blocking IO)
而在用户进程这边,整个进程会被阻塞。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,
然后kernel返回结果,用户进程才解除block的状态,重新运行起来。
所以,blocking IO的特点就是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被block了。
非阻塞IO(non-blocking IO)
进程调用read操作,如果IO设备没有准备好,立刻返回error,进程不阻塞.
用户可以再次发起系统调用,如果内核已经准备好,就阻塞,然后复制数据到用户空间
第一阶段是非阻塞的.
第二阶段是阻塞的,即内核空间和用户空间之间的赋值数据是阻塞的.
不建议使用,因为其缺点明显:
循环使用recv()大幅度推高CPU占用,
任务完成的响应延迟增大了,因为每过一段时间轮询一次read的操作,而任务可能在两次轮询之间的任意时间完成.
会导致整体数据吞吐量的降低.
多路复用IO(IO multiplexing)
有些地方也称这种IO方式为事件驱动IO(event driven IO)。
就是同时监控多个IO,有一个准备好了,就直接返回
以select为例
当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,
当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。
这个图和blocking IO的图其实并没有太大的不同,事实上还更差一些。因为这里需要使用两个系统调用\(select和recvfrom\),
而blocking IO只调用了一个系统调用\(recvfrom\)。但是,用select的优势在于它可以同时处理多个connection。
优点:
使用select()的事件驱动值使用单线程(进程)执行,占用资源少,不消耗太多CPU,可以同时服务多个连接
缺点:
该模型将事件探测和事件响应夹杂在一起,一旦事件响应的执行体过大,对整个模型是灾难性的
一般情况,select最多能监听1024个fd(文件描述符)(可以修改,但是不建议),但是由于select采用轮询的方式,当管理的IO多了,每次都要遍历全部fd,效率低下.相当于是,文件准备好了,再去遍历一边看看哪个好了,返回数据
epoll没有管理的fd的上限,且是回调机制,不需遍历,效率很高,相当于是文件准备好了,直接去调准备好的数据返回,
异步IO(Asynchronous I/O)
用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。
不常用
Python中的IO多路复用
selectors库