IO多路复用 Select和epoll
2022-01-01 本文已影响0人
xian_cheng
背景说明
I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
epoll跟select都能提供多路I/O复用的解决方案。在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSIX所规定,一般操作系统均有实现
Select
select.pngSelect模型使用单个线程监听客户端连接请求,连接建立成功后注册读写事件到Selector中,Selector调用操作系统内核的方法注册读写事件,然后Selector阻塞等待读写事件。
客户端发送数据到服务端,操作系统内核轮训socket连接的读写事件,当存在读写事件时,生成对应的可读列表readList和可写列表writeList,应用层遍历读写事件列表readList和writeList,做相应的读写操作,进行后续的业务处理。
从上面的流程可以看出,主要存在两个问题:
(1) 轮训,每次调用时都会对连接进行线性遍历,所以随着连接数的增加会造成遍历速度慢的“线性下降性能问题”,性能瓶颈可能会出现。
(2) 连接数量。连接数量首先影响轮训效率,其次,如果连接数很多,连接在应用层和内核层的相互copy,也会有一定的性能影响。
epoll
epoll.pngepoll模型主要解决了select模型的两个问题:
- 轮训问题, epoll模型是在创建一个socket连接时,会在操作系统内核层的epoll模型中 注册此socket连接的callback函数,当后续检测到存在读写事件时,通过此回调函数,将事件记录到epoll模型中。
- 连接句柄符复制问题。 epoll模型在创建socket连接时,就会把连接建立的句柄符保存到操作系统内核层的epoll模型中,这样就避免后续在应用层和内核层来回复制从而产生的性能问题。
epoll模型在操作系统内核层的实现可以参考这篇文章(深入理解select、poll和epoll及区别)(https://blog.csdn.net/wteruiycbqqvwt/article/details/90299610)
总结思考
- epoll 的事件通知机制,内核和应用层的mmp机制还需要具体看下。
- 通过epoll 和 select的对比,可以发现方案是逐步演化的,联系到目前的业务架构中也是类似的,架构不是一步到位的,避免过度设计,针对痛点问题及时调整即可。
- epoll的事件通知机制类似于设计模式的观察者模式,spring的事件通知机制,可以极大的解耦和提高性能,后面的业务中也要着重学习这种设计思路。