1.阻塞IO
在linux中,默认情况下所有的socket都是blocking
当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据。对于network io来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的UDP包),这个时候kernel就要等待足够的数据到来。
而在用户进程这边,整个进程会被阻塞。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。
所以,blocking IO的特点就是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被block了。(几乎所有的IO接口都是阻塞型的)
注:所谓阻塞型接口是指系统调用(一般是IO接口)不返回调用结果并让当前线程一直阻塞,只有当该系统调用获得结果或者超时出错时才返回。
因此,在面对大量客户访问的时候,会出现较多阻塞占用系统资源,多线程、线程池可以解决小规模的服务问题,大量访问的解决会受到限制
2.非阻塞IO(non-blocking IO)
linux下,可以设置socket使其变为非阻塞,这时的操作指令的执行如下图:
可以看出,用户发出请求,kernel立刻就会回应(可能是准备好的数据或者没准备好数据回应一个error)并不会阻塞,等到kernel中的数据准备好了,并且又再次收到了用户进程的请求(期间client会不停的发出请求,这个过程称为轮询),就将数据拷贝到了用户内存(这一阶段仍然是阻塞的),然后返回。
优点:刚开始请求的时候原本会阻塞,而non-blocking 设置之后,这个阶段可以去做其他的事情
缺点:@1.循环调用recv()将大幅度推高CPU占用率
@2.任务完成的响应延迟增大了,因为每过一段时间才去轮询一次read操作,而任务可能在两次轮询之间的任意时间完成。这会导致整体数据吞吐量的降低。
3. 多路复用IO(IO mutiplexing)
select/epoll:不断轮询socket,收到通知才告诉用户
优点:一个进程就可以处理多个IO,(不适用于单个连接,因为他处理的并不快)
话不多说,上图:
异步IO:
用户发出请求,kernel就立刻返回(虽然什么内容都没),等到它将数据都拷贝到用户内存,就给用户发一个信号signal(拷贝完了,过来拿吧)