如何理解io模型?

从计算机结构视角来说,I/O描述了计算机系统与外部设备之间的通信的过程,一个进程的内存空间分为内核空间和用户空间,我们平常程序能直接操作的都是用户空间,内核空间的操作只能是由操作系统完成。从应用程序的视角来看,我们想进行io操作,必须是想操作系统发起io请求,然后操作系统去执行具体的io操作,当程序发起io请求之后,会有下面3个步骤:
1:内核等待io设备准备数据
2:内核把数据搬到用户空间
3:程序去看看数据是不是准备好了
这种方式,归根都是阻塞IO。
 
还有一种方式:
1:内核等待io设备准备数据
2:内核把数据搬到用户空间
3:内核通知程序数据准备好了,你去取数据
这种方式才是真正的非阻塞IO,但是这种非阻塞,是依赖于操作系统是否提供了 (Asynchronous I/O),但是这种非阻塞我们用的不多,一个是依赖操作系统,并不是所有操作系统都引入了,比如linux从内核2.6的版本才开始引入。下面我讨论的是第一种方式的情况下的同步异步阻塞和不阻塞。
 
然后在线程发起IO请求之后,线程在干什么?线程可以有几种选择:
1.可以直接等在哪里,直到内核把数据搬到用户空间,然后再接着干别的---这个叫做同步阻塞
2.线程可以干别的,然后不停的来看看内核的数据是不是到位了。--这个叫同步非阻塞
 
上面的2种方式,虽然有改进,但是对cpu的消耗还是很大的,因为需要不停的放下手上的事情来追问内核你数据准备好了么?这个时候程序就想了个办法,既然你内核不主动通知我们数据准备好了,我就派个人专门去监控内核数据然后等对应线程的数据准备好了就去通知对应程序。所以就有了io多路复用既Reactor模式也叫异步阻塞IO,java中的Selector和linux中的epoll都是这种模式;
 
Selector的原理就是,业务程序们请了个看门的叫做select,他拿了个本子(数据结构),记录了业务线程们的句柄和对应socket对象的关系。然后去各种轮训代替业务线程去问内核,有没有socket的数据准备好了(还有异常的情况),好了之后,select就去通知(一般是回调函数更好)业务线程数据好了。
epoll原理是针对selector做了优化,大概就是业务线程们嫌弃selector事情做得不够完美,因为他每次都要从本子上一个一个线程去问,线程太多的时候问的速度就很慢。然后换了个人来管理,这个epoll拿的本子就比较先进(换了红黑树的存储)这样在找业务线程和socket的速度就加快了。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
posted @ 2023-05-06 14:12  Jojojojoo  阅读(10)  评论(0编辑  收藏  举报