Redis中单机数据库的实现

1. 内存操作层 zmalloc 系接口

redis为了优化内存操作, 封装了一层内存操作接口. 默认情况下, 其底层实现就是最简朴的libc中的malloc系列接口. 如果有定制化需求, 可以通过配置方式, 将底层内存操作的实现更换为tcmalloc或jemalloc库.

redis封装的这一层接口, 其接口定义与默认实现在zmalloc.h与zmalloc.c中. 其默认实现支持在O(1)复杂度的情况下返回内存块的大小. 具体实现上的思路也十分简朴: 就是在内存块头部多分配一个long的空间, 将内存块的大小记录在此.

zmalloc

在zmalloc.c中可以看到, 底层接口的具体实现可选的有tcmalloc与jemalloc两种:

/* Explicitly override malloc/free etc when using tcmalloc. */ #if defined(USE_TCMALLOC) #define malloc(size) tc_malloc(size) #define calloc(count,size) tc_calloc(count,size) #define realloc(ptr,size) tc_realloc(ptr,size) #define free(ptr) tc_free(ptr) #elif defined(USE_JEMALLOC) #define malloc(size) je_malloc(size) #define calloc(count,size) je_calloc(count,size) #define realloc(ptr,size) je_realloc(ptr,size) #define free(ptr) je_free(ptr) #define mallocx(size,flags) je_mallocx(size,flags) #define dallocx(ptr,flags) je_dallocx(ptr,flags) #endif

内存分配接口的实现如下, 从代码中可以看出, 其头部多分配了PREFIX_SIZE个字节用于存储数据区的长度.

void *zmalloc(size_t size) { void *ptr = malloc(size+PREFIX_SIZE); if (!ptr) zmalloc_oom_handler(size); #ifdef HAVE_MALLOC_SIZE update_zmalloc_stat_alloc(zmalloc_size(ptr)); return ptr; #else *((size_t*)ptr) = size; update_zmalloc_stat_alloc(size+PREFIX_SIZE); return (char*)ptr+PREFIX_SIZE; #endif } 2. 事件与IO多路复用

redis中的事件处理机制和内存分配一样, 也是糊了一层接口. 其底层实现是可选的. 默认情况下, 会自动选取当前OS平台上速度最快的多路IO接口, 比如在Linux平台上就是epoll, 在Sun/Solaris系列平台上会选择evport, 在BSD/Mac OS平台上会选择kqueue, 实再没得选了, 会使用POSIX标准的select接口. 保证起码能跑起来

事件处理对各个不同底层实现的包裹, 分别在ae_epoll.c, ae_evport.c, ae_kqueue, ae_select.c中, 事件处理器的定义与实现在ae.h与ae.c中.

ae.h中, 定义了

事件处理回调函数的别名: ae***Proc函数指针类型别名

文件事件与定时事件两种结构: aeFileEvent与aeTimeEvent

结构aeFiredEvent, 代表被触发的事件

结构aeEventLoop, 一个事件处理器. 包含一个事件循环.

分别如下:

#define AE_NONE 0 /* No events registered. */ #define AE_READABLE 1 /* Fire when descriptor is readable. */ #define AE_WRITABLE 2 /* Fire when descriptor is writable. */ #define AE_BARRIER 4 /* With WRITABLE, never fire the event if the READABLE event already fired in the same event loop iteration. Useful when you want to persist things to disk before sending replies, and want to do that in a group fashion. */ // ..... /* Types and data structures */ typedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask); typedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData); typedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData); typedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop); /* File event structure */ typedef struct aeFileEvent { int mask; /* one of AE_(READABLE|WRITABLE|BARRIER) */ aeFileProc *rfileProc; // 可读回调 aeFileProc *wfileProc; // 可写回调 void *clientData; // 自定义数据 } aeFileEvent; /* Time event structure */ typedef struct aeTimeEvent { long long id; /* time event identifier. */ // 定时事件的编号 long when_sec; /* seconds */ // 触发时间(单位为秒) long when_ms; /* milliseconds */ // 触发时间(单位为毫秒) aeTimeProc *timeProc; // 定时回调 aeEventFinalizerProc *finalizerProc; // 事件自杀析构回调 void *clientData; // 自定义数据 struct aeTimeEvent *prev; // 链表的前向指针与后向指针 struct aeTimeEvent *next; } aeTimeEvent; /* A fired event */ typedef struct aeFiredEvent { // 描述了一个被触发的事件 int fd; // 文件描述符 int mask; // 触发的类型 } aeFiredEvent; /* State of an event based program */ typedef struct aeEventLoop { // 描述了一个热火朝天不停循环的事件处理器 int maxfd; // 当前监控的文件事件中, 文件描述符的最大值 int setsize; // 事件处理器的最大容量(能监听的文件事件的个数) long long timeEventNextId; // 下一个到期要执行的定时事件的编号 time_t lastTime; // 上一次有回调函数执行的时间戳, 用以防止服务器时间抖动或溢出 aeFileEvent *events; // 所有注册的文件事件 aeFiredEvent *fired; // 所有被触发的文件事件 aeTimeEvent *timeEventHead; // 定时事件链表 int stop; // 急停按钮 void *apidata; // 底层多路IO接口所需的额外数据 aeBeforeSleepProc *beforesleep; // 一次循环结束, 所有事件处理结束后, 要执行的回调函数 aeBeforeSleepProc *aftersleep; // 一次循环开始, 在执行任何事件之前, 要执行的回调函数 } aeEventLoop;

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpsydf.html