Redis内核之事件驱动作者:cf (360电商技术组) 概述Redis实现了自己的事件驱动,与开源事件库libevent、libev一样,都是基于I/O多路复用技术实现的。出于性能和代码精炼两方面考虑,redis未像memcache一样使用libevent或libev成熟的事件库(libevent/libev为了其通用性增加了很多扩展功能降低了使用它的性能,且代码量相比redis来说是大很多的)。 它主要支持了epoll、select、kqueue、以及基于Solaris的event ports。主要提供了对两种类型的事件驱动: 1、I/O事件,包括读事件和写事件。 2、定时器事件,包括一次性定时器和循环定时器。
源码分析主要文件有:ae.c ae.h ae_epoll.c ae_evport.c ae_kqueue.c ae_select.c, 其中ae.c是事件处理模块主体,ae_epoll.c ae_kqueue.c ae_select.c ae_evport.c是事件处理的四种实现方式,分别对应了epoll、select、kqueue、event ports,提供了相同的接口。 #ifdef HAVE_EVPORT #include "ae_evport.c" #else #ifdef HAVE_EPOLL #include "ae_epoll.c" #else #ifdef HAVE_KQUEUE #include "ae_kqueue.c" #else #include "ae_select.c" #endif #endif #endif
ae.c分析redis的ae事件驱动库主要逻辑在ae.c中,其中根据使用的系统事件接口分别选择include ae_epoll.c或其他的文件。用到的主要数据结构在ae.h中定义。
主要数据结构创建: aeCreateEventLoop首先创建一个aeCreateEventLoop对象。该对象需要一个最大文件描述符作为参数setSize,这个参数的意义需要了解ae的数据存放结构。在aeEventLoop结构中有两个数组(服务器程序惯用提前分配好内存然后用index映射到相应位置的做法),这两个数组的大小就是这里的参数值。 ae会创建一个 setSize*sizeof(aeFileEvent) 以及一个 setSize*siezeof(aeFiredEvent) 大小的内存,用文件描述符作为其索引,可以达到O(1)的速度找到事件数据所在位置。
准备系统提供的事件模型接口,以epoll为例。ae提供了一个统一的结构名aeApiState。在包装epoll的aeApiState中有一个epfd表示epoll占用的fd,一个epoll_event *events,其实也是一个aeApiState数组,和aeFiredEvent对应,当epoll_wait()返回时,会将pending的文件描述符的信息放在aeFiredEvent数组中,包括fd和mask事件类型,此时的aeFiredEvent不是以fd作为下标的,而是把这个数组当成一个缓冲区,存放epoll_wait()返回的所有fd,同时用epoll_event数组存放epoll_wait()返回的epoll_data数据,用其数据可以填充aeFiredEvent数组的内容供ae使用找到pending的aeFileEvent对象,并在下一次进入epoll_wait()前处理完。这样完成了对epoll数据封装。
typedef struct aeApiState { int epfd; struct epoll_event *events; } aeApiState;
aeCreateFileEvent创建I/O事件时需要指定要注册的文件的文件描述符fd,以及要禁用词语的事件类型mask。ae先通过fd找到其对应的aeCreateFileEvent对象所在内存位置。
typedef struct aeFileEvent { int mask; /* one of AE_(READABLE|WRITABLE) */ aeFileProc *rfileProc; aeFileProc *wfileProc; void *clientData; } aeFileEvent;
添加要禁用词语的事件类型mask fe- |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|