1 IO 模型之IO多路复用并发
解决NIO的思路就是降解无效的系统调用,如何降解呢?咱们一块儿来看看如下几种IO多路复用的解决思路。异步
1.1. IO多路复用之select/pollide
Select是内核提供的系统调用,它支持一次查询多个系统调用的可用状态,当任意一个结果状态可用时就会返回,用户进程再发起一次系统调用进行数据读取。换句话说,就是NIO中N次的系统调用,借助Select,只须要发起一次系统调用就够了。其IO流程以下所示:高并发
可是,select有一个限制,就是存在链接数限制,针对于此,又提出了poll。其与select相比,主要是解决了链接限制。性能
select/epoll 虽然解决了NIO重复无效系统调用用的问题,但同时又引入了新的问题。问题是:优化
用户空间和内核空间之间,大量的数据拷贝spa
内核循环遍历IO状态,浪费CPU时间3d
换句话说,select/poll虽然减小了用户进程的发起的系统调用,但内核的工做量只增不减。在高并发的状况下,内核的性能问题依旧。因此select/poll的问题本质是:内核存在无效的循环遍历。orm
1.2. IO多路复用之epollblog
针对select/pool引入的问题,咱们把解决问题的思路转回到内核上,如何减小内核重复无效的循环遍历呢?变主动为被动,基于事件驱动来实现。其流程图以下所示:
epoll相较于select/poll,多了两次系统调用,其中epoll_create创建与内核的链接,epoll_ctl注册事件,epoll_wait阻塞用户进程,等待IO事件。
epoll,已经大大优化了IO的执行效率,但在IO执行的第一阶段:数据准备阶段都仍是被阻塞的。因此这是一个能够继续优化的点。
2 IO 模型之信号驱动IO(SIGIO)
信号驱动IO与BIO和NIO最大的区别就在于,在IO执行的数据准备阶段,不会阻塞用户进程。以下图所示:当用户进程须要等待数据的时候,会向内核发送一个信号,告诉内核我要什么数据,而后用户进程就继续作别的事情去了,而当内核中的数据准备好以后,内核立马发给用户进程一个信号,说”数据准备好了,快来查收“,用户进程收到信号以后,立马调用recvfrom,去查收数据。
乍一看,信号驱动式I/O模型有种异步操做的感受,可是在IO执行的第二阶段,也就是将数据从内核空间复制到用户空间这个阶段,用户进程仍是被阻塞的。
综上,你会发现,无论是BIO仍是NIO仍是SIGIO,它们最终都会被阻塞在IO执行的第二阶段。那若是能将IO执行的第二阶段变成非阻塞,那就完美了。
3 IO 模型之异步IO(AIO)
异步IO真正实现了IO全流程的非阻塞。用户进程发出系统调用后当即返回,内核等待数据准备完成,而后将数据拷贝到用户进程缓冲区,而后发送信号告诉用户进程IO操做执行完毕(与SIGIO相比,一个是发送信号告诉用户进程数据准备完毕,一个是IO执行完毕)。其流程以下:
因此,之因此称为异步IO,取决于IO执行的第二阶段是否阻塞。所以前面讲的BIO,NIO和SIGIO均为同步IO。