IO模型:BIO到NIO再到epoll
以网络IO为例子:如果程序读取队列时,队列没有数据,那么就会出现阻塞。
假设第一个app负责处理两个队列,第一个socket阻塞,那它就无法处理第二个正常的socket,
因此:一个socket对应一个请求队列,一个队列对应一个线程。=》费线程,并发数很多时性能差,但也不是绝对不好
改进:一个程序读两个队列,可以在程序中写一个判断,如果检测到当前队列为空,就处理其他的队列
NIO访问内核不会阻塞,但返回值需要用户自己判断,这样容易使得用户态频繁访问内核态,造成系统资源调度的浪费。
如:一万个连接,只有4个非空,为了这4个队列,内核态被调度了1万次
解决思路:将一个连接看成一条路,如何只访问一次内核态,就知道哪些是空队列呢?
解决方法:将轮询从IO层放到内核中,程序想知道哪些是非空的,只要问一次内核:“请问哪些是非空队列?”即可。
多路复用器,可以看作一个电话,调用内核态:select、poll、epoll
内核态中的数据并不是内核返回的,而是程序搬运走的,而只要是程序自主参与IO中的R/W,和内核交互的,都叫同步模型。
select和poll的缺点:连接复制开销。如我有一万个连接,程序每次都问第一个连接、第二个连接、、、、 第一万个连接哪些是非空的,那么函数的参数就会传这一万个连接,特别是在fork子线程时,参数的复制开销会很大。
epoll的好处:内核弄一个小本本记录并更新小本本中的队列,每次被调用轮询小本本中的连接就行,而不用程序指定。
电话、小本本创建与维护三步骤:1,程序启动,打电话A叫内核创建空的小本本;2,程序打电话B叫内核查哪些连接,小本本把连接记录下来;3,程序打电话C:有非空的连接吗;4,如果新来了连接,程序会再一次打电话B,小本本更新连接。
小本本中的记录(连接的地址)用红黑树组织。
在java中,三个电话分别有如下对应关系:
A:epoll_create();B:epoll_ctl();C:epoll_wait(*list);
其中,list来的时候是空的,通过查询红黑树发现有非空队列后,程序会插入到list中返回去。=》epoll也是同步
epoll也还是在轮询,不浪费资源吗?
在redis和nginx中都有应用=》具体的以后再看,但总的来说epoll提高了计算机资源的利用率