在面试的过程中,经常会被问道I/O复用模型有哪些?它们的区别是什么?你拿它们做过什么项目?……
如果你连I/O复用模型都不知道,那么后面的问题也就不会被问到了,你这场面试已经岌岌可危了,至少在Linux方面公司是不会考虑你了。当然,如果你在其他方面特别突出,那还是可以挽回的。不要紧张,看完这篇文章后回去编写程序测试一下就会了。记得一定要编写代码哦,只知道理论知识是不行的,面试官一问就知道你几斤几两,没用过是掩盖不了的。具体代码我就不粘贴了,那样你就只看不动手了,哈哈……
首先让我们来看一下select内部调用机制
select->sys_select(从用户控件拷贝各个fd_set到内核空间)->core_sys_select->do_select(调用文件系统的 poll函数)
select的执行方式基本就是不停的调用poll,直到有需要的消息为止,如果select处理的socket很多,这其实对整个机器的性能也是一个消耗。
select、poll、epoll 区别总结
1、支持一个进程所能打开的最大连接数
select | 单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是32*32,同理64位机器上FD_SETSIZE为32*64),当然我们可以对其进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试。 |
poll | poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的 |
epoll | 虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接 |
2、FD剧增后带来的I/O效率问题
select | 因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。 |
poll | 同上 |
epoll | 因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。 |
3、消息传递方式
select | 内核需要将消息传递到用户空间,都需要内核拷贝动作 |
poll | 同上 |
epoll | epoll通过内核和用户空间共享一块内存(mmap)来实现的。内核微调。 |
总结
综上,在选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点。
1、表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。
2、select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善。
epoll
当一个进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体。当我们执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它放到准备就绪list链表里。一颗红黑树,一张准备就绪句柄链表,少量的内存cache,就帮我们解决了高并发下的socket处理问题。执行epoll_create时,创建了红黑树和就绪链表,执行epoll_ctl时,如果增加socket句柄,则检查在红黑树中是否存在该socket句柄,若存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。执行epoll_wait时立刻返回准备就绪链表里的数据即可。
LT(level-triggered)是epoll缺省的工作方式,并且同时支持block(阻塞)和no-block(非阻塞) socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行I/O操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误的可能性要小一点。传统的select/poll都是这种模型的代表.
ET(edge-triggered)是高速工作方式,只支持no-block(非阻塞) socket,它效率要比LT更高。ET与LT的区别在于,当一个新的事件到来时,ET模式下可以从epoll_wait调用中获取到这个事件,可是如果这次没有把这个事件对应的套接字缓冲区处理完,在这个套接字中没有新的事件再次到来时,在ET模式下是无法再次从epoll_wait调用中获取这个事件的。而LT模式正好相反,只要一个事件对应的套接字缓冲区还有数据,就总能从epoll_wait中获取这个事件。
因此,LT模式下开发基于epoll的应用要简单些,不太容易出错。而在ET模式下事件发生时,如果没有彻底地将缓冲区数据处理完,则会导致缓冲区中的用户请求得不到响应。Nginx默认采用ET模式来使用epoll。