I/O模型主要分为5种:阻塞I/O、非阻塞I/O、多路复用I/O、异步I/O、信号驱动I/O
阻塞I/O:等待数据与复制数据另个阶段都被阻塞。
ps:大部分的socket编程的接口都是阻塞I/O型:recv、recvfrom、listen、send、accept。
解决:采用多线程、多线程,开启线程池、进程池。
开多进程、多线程彼此之间互不影响,加入池可以有效限制其数量,使得系统不至于卡死。但是对于更庞大的服 务请求时,会受到限制。
非阻塞I/O:数据未到,不会等待,返回一个错误BlockingIOError;复制数据阶段不变。
缺点:
1. 循环调用recv()将大幅度推高CPU占用率;这也是我们在代码中留一句time.sleep(2)的原因,否则在低配主机下极容易出现卡机情况
2. 任务完成的响应延迟增大了,因为每过一段时间才去轮询一次read操作,而任务可能在两次轮询之间的任意时间完成。
这会导致整体数据吞吐量的降低。
多路复用I/O:select/epoll函数会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。
特点:实质与阻塞I/O同(一个是被时socket I/O阻塞,一个是被select阻塞),但其可同时处理多个用户进程, 适用于多个连接的场景,在单个连接时不适用。
缺点:
因为当需要探测的句柄值较大时,select()接口本身需要消耗大量时间去轮询各个句柄,
很多操作系统提供了更为高效的接口,如linux提供了epoll,BSD提供了kqueue,Solaris提供了/dev/poll
如果需要实现更高效的服务器程序,类似epoll这样的接口更被推荐。遗憾的是不同的操作系统特供的epoll接口有很大差异,
所以使用类似于epoll的接口实现具有较好跨平台能力的服务器会比较困难。
其次,该模型将事件探测和事件响应夹杂在一起,一旦事件响应的执行体庞大,则对整个模型是灾难性的。
异步I/O:用户端:发出信号,数据未到时,立即返回,执行其他操作;
服务端:进行等待与复制,完成后给对方发送信号。