redis 从0到1完整学习 (二十):IO多路复用之epoll

发布时间:2024年01月16日


1. 引言

前情提要:
《redis 从0到1完整学习 (一):安装&初识 redis》
《redis 从0到1完整学习 (二):redis 常用命令》
《redis 从0到1完整学习 (三):redis 数据结构》
《redis 从0到1完整学习 (四):字符串 SDS 数据结构》
《redis 从0到1完整学习 (五):集合 IntSet 数据结构》
《redis 从0到1完整学习 (六):Hash 表数据结构》
《redis 从0到1完整学习 (七):ZipList 数据结构》
《redis 从0到1完整学习 (八):QuickList 数据结构》
《redis 从0到1完整学习 (九):SkipList 数据结构》
《redis 从0到1完整学习 (十):RedisObject 数据结构》
《redis 从0到1完整学习 (十一):RedisObject 之 String 类型》
《redis 从0到1完整学习 (十二):RedisObject 之 List 类型》
《redis 从0到1完整学习 (十三):RedisObject 之 Set 类型》
《redis 从0到1完整学习 (十四):RedisObject 之 ZSet 类型》
《redis 从0到1完整学习 (十五):RedisObject 之 Hash 类型》
《redis 从0到1完整学习 (十六):内存回收之 key 过期处理策略》
《redis 从0到1完整学习 (十七):内存回收之内存淘汰策略》
《redis 从0到1完整学习 (十八):阻塞/非阻塞 IO》
《redis 从0到1完整学习 (十九):IO多路复用之select和poll》
之前介绍了阻塞、非阻塞 I/O 以及 IO 多路复用机制的 select 和 poll 的机制,本文主要介绍 epoll 的主要流程和原理。

2. redis 源码下载

Redis 源码可以点击这里下载,方便查看其中定义的一些数据结构。
在这里插入图片描述

3. IO 多路复用模型

回顾上节的 IO 多路复用模型:
在这里插入图片描述
在 IO 多路复用机制下,当没有 IO 事件发生时,调用会阻塞进程等待,直到至少有一个文件描述符准备好进行读写操作为止。一旦某个文件描述符就绪(例如有数据可读或可写),系统会立即通知应用程序,并返回所有准备好的文件描述符列表,然后应用程序就可以针对性地对这些已就绪的文件描述符进行相应的读取或写入操作。

下面看下 epoll 的实现原理。

4. epoll

epoll 是 Linux 内核提供的一种高效的 I/O 多路复用机制,它是 select 和 poll 的增强版,在处理大量并发连接时表现更优。epoll 通过解决传统多路复用方法的性能瓶颈来提高服务器端应用程序处理高并发连接的能力。

下面结合 epoll 的整体流程在介绍
在这里插入图片描述
图片参考:https://www.processon.com/view/5e8524f3e4b0a2d87028133b

4.1 事件注册和管理

epoll 使用一个内核级别的数据结构(通常是红黑树)来存储待监控的文件描述符集合,而不是每次调用都复制整个集合到内核空间。

(1)应用程序通过 epoll_create 创建一个 epoll 实例(返回一个文件描述符)

int epoll_create(int size);

(2)使用 epoll_ctl 函数向 epoll 实例中添加、修改或删除要监听的文件描述符及其事件类型

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

(3)事件类型如 EPOLLIN、EPOLLOUT 等,具体数据结构如下:

// epoll.h
typedef union epoll_data {
  void *ptr;
  int fd;
  uint32_t u32;
  uint64_t u64;
} epoll_data_t;

struct epoll_event {
  uint32_t events;   /* Epoll events */
  epoll_data_t data; /* User data variable */
} __EPOLL_PACKED;

事件类型如下:

events描述
EPOLLIN可读
EPOLLOUT可写
EPOLLERR该文件描述符发生错误
EPOLLHUP该文件描述符被挂断

4.2 事件触发通知

当任何一个被监视的文件描述符上有事件发生时,内核会将其标记为就绪,并将就绪的描述符数量更新到 epoll 实例上。

应用程序通过调用 epoll_wait 来等待并获取已就绪的文件描述符列表,该函数仅返回实际就绪的描述符,避免了无谓的遍历。

int epoll_wait(int epfd, struct epoll_event* events, int maxevents. int timeout);

其中,事件通知的模式有两种:

  • 水平触发(LT, Level Triggered)。epoll 默认支持水平触发模式,即只要文件描述符处于可读/写状态,每次调用 epoll_wait 都会返回这个描述符。
  • 边缘触发(ET, Edge Triggered)。边缘触发模式下,epoll 只在状态变化的时候返回一次,因此需要应用程序能够一次性处理完所有的数据,否则可能错过后续的事件。

4.3 总结

  • epoll 不再受限于固定的最大文件描述符数,且由于其内部实现优化,对于大量的活跃连接,性能优于 select 和 poll。
  • 在活跃连接较少的情况下,epoll 的开销可能会比 select/poll 略大,但随着活跃连接数增加,优势愈发明显。
  • 内存映射(mmap)技术应用:在某些情况下,当从 epoll_wait 获得就绪的文件描述符时,可以通过内存映射技术减少系统调用过程中的数据复制开销,进一步提升效率。

总结来说,epoll 提供了一种更加强大且高效的 I/O 事件通知机制,它非常适合构建高性能网络服务器,尤其是在处理大规模并发连接时,可以有效降低上下文切换以及资源占用,从而达到更高的吞吐量和更低的延迟。

5. 参考

《redis 从0到1完整学习 (一):安装&初识 redis》
《redis 从0到1完整学习 (二):redis 常用命令》
《redis 从0到1完整学习 (三):redis 数据结构》
《redis 从0到1完整学习 (四):字符串 SDS 数据结构》
《redis 从0到1完整学习 (五):集合 IntSet 数据结构》
《redis 从0到1完整学习 (六):Hash 表数据结构》
《redis 从0到1完整学习 (七):ZipList 数据结构》
《redis 从0到1完整学习 (八):QuickList 数据结构》
《redis 从0到1完整学习 (九):SkipList 数据结构》
《redis 从0到1完整学习 (十):RedisObject 数据结构》
《redis 从0到1完整学习 (十一):RedisObject 之 String 类型》
《redis 从0到1完整学习 (十二):RedisObject 之 List 类型》
《redis 从0到1完整学习 (十三):RedisObject 之 Set 类型》
《redis 从0到1完整学习 (十四):RedisObject 之 ZSet 类型》
《redis 从0到1完整学习 (十五):RedisObject 之 Hash 类型》
《redis 从0到1完整学习 (十六):内存回收之 key 过期处理策略》
《redis 从0到1完整学习 (十七):内存回收之内存淘汰策略》
《redis 从0到1完整学习 (十八):阻塞/非阻塞 IO》
《redis 从0到1完整学习 (十九):IO多路复用之select和poll》

欢迎关注本人,我是喜欢搞事的程序猿; 一起进步,一起学习;

也欢迎关注我的wx公众号:一个比特定乾坤

文章来源:https://blog.csdn.net/qq_36803941/article/details/135625553
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。