同步阻塞IO、同步非阻塞IO、异步阻塞(IO多路复用)、异步非阻塞IO区别
常用4种IO模型
(1)同步阻塞IO(Blocking IO):即传统的IO模型。
(2)同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。
注意这里所说的NIO并非Java的NIO(New IO)库。
(3)IO多路复用(IO Multiplexing):即经典的Reactor设计模式,有时也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型。
(4)异步IO(Asynchronous IO):即经典的Proactor设计模式,也称为异步非阻塞IO。
I/0 操作基本概念
1数据准备,将数据加载到内核缓存(数据加载到操作系统)
2将内核缓存中的数据加载到用户缓存(从操作系统复制到应用中)
3同步和异步的概念描述的是用户线程与内核的交互方式
4阻塞和非阻塞的概念描述的是用户线程调用内核IO操作的方式
同步阻塞IO
同步阻塞IO模型是最简单的IO模型,用户线程在内核进行IO操作时被阻塞。
用户线程通过系统调用read发起IO读操作,由用户空间转到内核空间。内核在等待数据的时候将用户线程挂起造成阻塞,内核等到数据包到达后,然后将接收的数据拷贝到用户空间,完成read操作。
同步非阻塞IO
同步非阻塞IO是在同步阻塞IO的基础上,将socket设置为NIO(NONBLOCK)。
由于socket是非阻塞的方式,因此用户线程发起IO请求时立即返回。返回的是可能是一个code码代表数据正在处理中,此时线程一直循环轮询发送IO请求,直到数据在内核准备完毕才真正获取数据。此处非阻塞的意思是:线程轮询一直请求内核占用内核时间片而不被挂起的状态。效率上节省了取消用户线程挂起的资源消耗,但也伴随着大量CPU运算。对用户来讲可能比较快的接收到请求的数据。
IO多路复用(异步阻塞)
IO多路复用模型是建立在内核提供的多路分离函数select基础之上的,
使用select函数可以避免同步非阻塞IO模型中轮询等待的问题。
用户首先将需要进行IO操作的socket添加到select中,然后阻塞等待select系统调用返回。
当数据到达时,socket被**,select函数返回。用户线程正式发起read请求,读取数据并继续执行。
从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,
甚至还多了添加监视socket,以及调用select函数的额外操作,效率更差。
但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。
用户可以注册多个socket,然后不断地调用select读取被**的socket,即可达到在同一个线程内同时处理多个IO请求的目的。
而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。
然而,使用select函数的优点并不仅限于此。
虽然上述方式允许单线程内处理多个IO请求,但是每个IO请求的过程还是阻塞的(在select函数上阻塞),平均时间甚至比同步阻塞IO模型还要长。
如果用户线程只注册自己感兴趣的socket或者IO请求,然后去做自己的事情,等到数据到来时再进行处理,则可以提高CPU的利用率。
异步IO(非阻塞)
“真正”的异步IO需要操作系统更强的支持。
在IO多路复用模型中,事件循环将文件句柄的状态事件通知给用户线程,由用户线程自行读取数据、处理数据。
而在异步IO模型中,当用户线程收到通知时,数据已经被内核读取完毕,并放在了用户线程指定的缓冲区内,内核在IO完成后通知用户线程直接使用即可。
相比于IO多路复用模型,异步IO并不十分常用,不少高性能并发服务程序使用IO多路复用模型+多线程任务处理的架构基本可以满足需求。
况且目前操作系统对异步IO的支持并非特别完善,更多的是采用IO多路复用模型模拟异步IO的方式。