图1-16
select模式有二个问题,
- 就是每次调用select都需要将进程加入到所有监视器socket的等待队列,每次唤醒都需要从等待队列中移除,这里涉及到两次遍历,有一定的性能开销。
- 进程被唤醒后,并不知道哪些socket收到了数据,所以还需要遍历一次所有的socket,得到就绪的socket列表
由于这两个问题产生的性能影响,所以select默认规定只能监视1024个socket,虽然可以通过修改监视的文件描述符数量,但是这样会降低效率。而poll模式和select基本是一样,最大的区别是poll没有最大文件描述符限制。
1.6 Linux中的epoll模型有没有更加高效的方法,能够减少遍历也能达到同时监听多个fd的目的呢?epoll模型就可以解决这个问题。
epoll 其实是event poll的组合,它和select最大的区别在于,epoll会把哪个socket发生了什么样的IO事件通知给应用程序,所以epoll实际上就是事件驱动,具体原理如图1-17所示。
在epoll中提供了三个方法分别是epoll_create、epoll_ctl、epoll_wait。具体执行流程如下
- 首先调用epoll_create方法,在内核创建一个eventpoll对象,这个对象会维护一个epitem集合,它是一个红黑树结构。这个集合简单理解成fd集合。
- 接着调用epoll_ctl函数将当前fd封装成epitem加入到eventpoll对象中,并给这个epitem加入一个回调函数注册到内核。当这个fd收到网络IO事件时,会把该fd对应的epitem加入到eventpoll中的就绪列表rdlist(双向链表)中。同时再唤醒被阻塞的进程A。
- 进程A继续调用epoll_wait方法,直接读取epoll中就绪队列rdlist中的epitem,如果rdlist队列为空,则阻塞等待或者等待超时。
从epoll的原理中可以得知,由于rdlist的存在,使得进程A被唤醒后知道哪些Socket(fd)发生了IO事件,从而在不需要遍历的情况下获取所有就绪的socket连接。
图1-17
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Mic带你学架构! 如果本篇文章对您有帮助,还请帮忙点个关注和赞,您的坚持是我不断创作的动力。欢迎关注同名微信公众号获取更多技术干货!