通过我们上面对NIO的了解,我们可以知道,NIO多少存在着一些不够好的地方,因为反复的轮训也是很消耗cpu资源的。如果饭店的人少还好说,但是如果饭店人多起来了,比如说来了几百个人,那每个人时不时就要发起一次询问请求,那老板管不过来啊,cpu占用率也会非常高。于是,IO多路复用就出现了,IO多路复用可以说是目前用的最多的一个IO模型,在不同的操作系统内核,也有不同的实现方式,在这篇文章中,我们IO多路复用的大概思想,至于详细介绍,后面会用一篇文章来详细的介绍IO多路复用
IO多路复用,实际上,是通过IO请求都通过一个selector来管理,用户进程的IO请求就不直接发给内核处理程序了,而是注册到这个selector上面,由selector来告诉内核需要哪些数据,然后定时的去查询内核程序,我这个selector上需要的数据,有哪些准备好了,然后再由selector告诉那些准备好了的用户线程,让该用户线程去拷贝数据。在非阻塞IO中,不断地询问状态时通过用户线程去进行的,而在IO多路复用中,询问每个状态是内核在进行的,在IO请求非常多的时候,这个效率要比用户线程轮询要高的多。
就像是你带手机去饭店吃饭,现在这家饭店的老板由于生意越来越好,人越来越多,他有点管理不过来了,于是他请了几个服务员(selector)协助管理,然后现在饭店客户的点餐都是告诉服务员,我需要什么菜,然后服务员把xx桌客户的菜,记在自己的单子上。然后服务员告诉厨房他这个单子上需要哪些菜,让厨房去做。。服务员定时问厨房看看有哪些菜已经准备好了,然后告知15号桌和89号桌客人你们的菜已经好了,请来前台端一下,然后你就去前台端菜,端菜的阶段是阻塞的。
来比较一下IO多路复用和NIO,我们可以发现,当IO请求多的时候,IO多路复用效率无疑是更高的。因为对于用户线程来说,你点完菜就可以一直玩手机了,不用因为一直问老板而分心分神,耽误你打王者,因为菜好了,服务员会通知你
信号驱动IO通过我们上面两种IO模型的了解,我们可以知道,不管是NIO还是IO多路复用,本质上还是轮询,只不过NIO是用户线程轮询,IO多路复用是委托给selector让他来轮询,那有没有什么办法能让内核主动通知数据好了没。所以,信号驱动IO出现了。信号信号,顾名思义,就是会有一个信号通知你数据已经准备好了,不用你一直去问。信号驱动IO,用户线程发出一个请求告诉内核我需要什么数据,数据准备好了你告诉我一声,然后内核就会记录下这个请求,内核准备好了之后会主动通知用户线程去执行拷贝数据,数据拷贝阶段是阻塞的,需要等数据拷贝完才能做其他的事。
就像是你带手机去吃饭,你点好菜之后,你就只管玩手机了,啥也不用管,就等老板通知你,期间你想干啥就干啥,等到菜准备好了,老板会大声说(内核主动通知用户进程),xxx你的鱼香肉丝已经准备好了,请过来前台端一下,这个时候你要自己去端(数据拷贝),这个端菜的阶段,你期间啥都干不了,也不能玩手机,所以信号驱动IO的第二个阶段也是阻塞的。
我们对比信号驱动IO和NIO,可以发现最重要的区别就是NIO是用户主动询问内核数据好了吗,而信号驱动IO是内核主动通知用户数据已经好了,这就改善了上面说的NIO的问题。
全异步IO全异步IO是最理想的一种IO模型,所谓全异步IO就是,用户进程发起了一个IO请求,接下来可以干其他的事了,不需要等内核准备好,也不需要执行数据拷贝,数据异步拷贝到用户空间之后,用户进程直接拿来用就行了,这两个阶段都是由内核自动完成。完全不用用户线程操心这些事。
前面四种IO模型实际上都属于同步IO,只有最后一种才是是真正的异步IO,因为不管是是IO多路复用还是信号驱动,IO操作的第2个阶段都会让用户线程阻塞,也就是内核进行数据拷贝的过程都会让用户线程阻塞。
举个例子就像是,你去饭店吃饭,点好餐之后,你就可以玩手机了,饭菜做好之后,服务员会把饭菜端到你的面前,你也不需要自己去端,你需要点餐和吃饭就行了,其他的你都不用管。简单来说,就是发出请求之后,只需要等待数据完成直接使用,等待期间,你可以做其他的事。整个过程完全的异步,体验最好。
全异步IO虽然非常牛逼,但是现在还不是很成熟,支持全异步IO的操作系统和框架也还不是很多,所以用的也不是很多。我们只需要了解一下就行了
总结