本文不涉及具体代码,只分析Linux IO演化的心路历程,学习资料来源网络,不保证必定正确,如有错误,欢迎指出。网络
BIOsocket
服务端建立socket(80端口),文件描述符3号。函数
当线程调用accept时,阻塞等待3 fd链接就绪。学习
网卡(80端口)收到数据,将数据写入内存,向cpu发出中断信号,内核得知3 fd有新数据,cpu调用中断程序响应中断。spa
线程唤醒,文件描述符为4号的socket,这时候就要新建线程T1去循环read(阻塞) 4fd了,由于主线程要负责链接。线程
弊端:一个线程处理一个链接,当有大量短链接的时候,就会有大量线程新建和消亡,频繁地状态切换,耗费大量资源。不过适用于长链接,且每一个链接有大量数据交互的状况。3d
是否是可让一个线程去处理多个链接呢?这样的入口点在哪里呢?blog
答曰:在read非阻塞,线程read 4fd,没有数据,那就去read 5fd,这样循环遍历。队列
因而有了非阻塞式IO事件
如今是能够一个线程处理多个链接了,可是又出现了新的问题。
1.当有大量有效链接时(指由数据要交互),一个线程要read 4fd,接着read 5fd,这样处理不及时。
2.当有大量无效链接时,一个线程频繁的read(系统调用),意味着要频繁的从用户态切换到内核态,这也耗费大量资源。
是否是能够减小read的调用次数呢?等到真的有数据来的时候再read。
因而有了IO多路复用的select模型
线程调用select,把要监听的socket和对应的期待事件告诉内核,而后阻塞在全部的fd上,当内核发现有事件发生时,再唤醒对应fd上的线程,然后线程就能够去read。
可是,线程只知道监听的全部socket上,某些有数据,殊不知道是哪一个,因此要挨个遍历(每一个socket是非阻塞的)。当有大量无效链接时,这一轮当中依然有 不少无效的read。
处理完一轮以后,进入到下一轮的select,而每一轮的select都要把要监听的socket的fd传给内核,数目一大,这成本也很高。
基于这两方面缘由,select的上限设定为1024。(poll模型基本与select同样,只是有少许改进,如不限数量(仍然受限于物理),将略过)
其实fd传一次就够了吧?之后的每一次都是在上一次的基础上,要新增就新增,要减掉就减掉,不然要监听仍是和上一次同样。
是否是能够省去无效的read呢?要是我醒来以后,要read的每个socket都是有效的就行了!
因而有了IO多路复用的epoll模型
epoll_create()获得一个5号epfd指向的是一个mmap共享空间(有关mmap,零拷贝的内容在下一篇讲解)。
接着获得一个7号fd的socket(NONBLOCK),经过epoll_ctl将它加到5号epfd的红黑树上。之后每一次要加,就经过调用这个函数加,文件描述符只用传一次。
epoll_wait(5)线程在eventpoll上阻塞等待(能够设置超时参数)。
当数据到来,5 epfd的红黑树上有事件发生时,中断程序将会把发生事件的socket加到rdlist这个双向链表中,而后唤醒5 epfd中eventpoll等待队列中的线程。
线程唤醒后就能够有效地遍历双向链表了。
epoll_wait的时候设置水平触发或者边缘触发
event.events = EPOLLIN | EPOLLET;//边缘触发,当有新数据到来时触发,若上一次没有读完,需等到下一次有新数据来。Netty中为边缘触发
event.events = EPOLLIN; // LT是默认模式,当socket中有数据(多是上一次遗留),epoll_wait便可返回。jdk nio中为水平触发