redis 5.0.7 源码阅读——双向链表
redis中双向链表相关的文件为:adlist.h与adlist.c
一、数据结构
redis里定义的双向链表,与普通双向链表大致相同
单个节点:
typedef struct listNode {
struct listNode *prev;
struct listNode *next;
void *value;
} listNode;
链表:
typedef struct list {
listNode *head;
listNode *tail;
void *(*dup)(void *ptr);
void (*free)(void *ptr);
int (*match)(void *ptr, void *key);
unsigned long len;
} list;
链表以函数指针的方式,实现了复制、销毁与比较的方法的多态。
迭代器:
typedef struct listIter {
listNode *next;
int direction;
} listIter;
迭代器中有个成员变量direction,用于表示当前遍历的方向。
大致结构:
/*
+-------------------+ +----------------> +--------------+ <-------+
|listNode *head |--------+ |listNode *prev|-->NULL |
+-------------------+ +--------------+ |
|listNode *tail |--------+ |listNode *next|----+ |
+-------------------+ | +--------------+ | |
|void *(*dup)(...) | | |void *value | | |
+-------------------+ | +--------------+ | |
|void (*free)(...) | | | |
+-------------------+ | | |
|int (*match)(...) | | | |
+-------------------+ +----------------> +--------------+ <--+ |
|unsigned long len | |listNode *prev|---------+
+-------------------+ +--------------+
|listNode *next|-->NULL
+--------------+
|void *value |
+--------------+
*/
二、创建
redis中创建一个初始双向链表比较简单,只要分配好内存,并给成员变量赋初值就可以了
list *listCreate(void)
{
struct list *list; if ((list = zmalloc(sizeof(*list))) == NULL)
return NULL;
list->head = list->tail = NULL;
list->len = ;
list->dup = NULL;
list->free = NULL;
list->match = NULL;
return list;
}
redis中提供了头插法、尾插法以及指定位置插入节点三种方式向链表中添加节点,与普通双向链表无异,此处不做详细叙述。
三、销毁
因链表中每个节点的value可能指向堆空间,故不能直接把list结构体free,这样会造成内存泄露。需要先将每个节点的value释放,才可以free结构体
清空所有节点:
void listEmpty(list *list)
{
unsigned long len;
listNode *current, *next; current = list->head;
len = list->len;
while(len--) {
next = current->next;
//若指定了销毁的函数,则使用指定的函数进行销毁value
if (list->free) list->free(current->value);
zfree(current);
current = next;
}
list->head = list->tail = NULL;
list->len = ;
}
销毁链表:
void listRelease(list *list)
{
listEmpty(list);
zfree(list);
}
同样,redis的链表提供了与普通链表相同的删除单个节点的操作,此处也不做叙述。
四、迭代器操作
redis中提供了获取迭代器的接口
listIter *listGetIterator(list *list, int direction)
{
listIter *iter; if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;
if (direction == AL_START_HEAD)
iter->next = list->head;
else
iter->next = list->tail;
iter->direction = direction;
return iter;
}
以AL_START_HEAD为例,生成好的迭代器结构如下:
/*
+-------------------+ +---> +--------------+ <-------+----+
|listNode *head |----+ |listNode *prev|-->NULL | |
+-------------------+ +--------------+ | | +--------------+
|listNode *tail |----+ |listNode *next|----+ | +--|listNode *next|
+-------------------+ | +--------------+ | | +--------------+
|void *(*dup)(...) | | |void *value | | | |int direction |
+-------------------+ | +--------------+ | | +--------------+
|void (*free)(...) | | | |
+-------------------+ | | |
|int (*match)(...) | | | |
+-------------------+ +---> +--------------+ <--+ |
|unsigned long len | |listNode *prev|---------+
+-------------------+ +--------------+
|listNode *next|-->NULL
+--------------+
|void *value |
+--------------+
*/
迭代器的next方法:
listNode *listNext(listIter *iter)
{
listNode *current = iter->next; if (current != NULL) {
if (iter->direction == AL_START_HEAD)
iter->next = current->next;
else
iter->next = current->prev;
}
return current;
}
调用一次之后的结构:
/*
+-------------------+ +---> +--------------+ <-------+
|listNode *head |----+ |listNode *prev|-->NULL |
+-------------------+ +--------------+ | +--------------+
|listNode *tail |----+ |listNode *next|----+ | +--|listNode *next|
+-------------------+ | +--------------+ | | | +--------------+
|void *(*dup)(...) | | |void *value | | | | |int direction |
+-------------------+ | +--------------+ | | | +--------------+
|void (*free)(...) | | | | |
+-------------------+ | | | |
|int (*match)(...) | | | | |
+-------------------+ +---> +--------------+ <--+----|----+
|unsigned long len | |listNode *prev|---------+
+-------------------+ +--------------+
|listNode *next|-->NULL
+--------------+
|void *value |
+--------------+
*/
再次调用:
/*
+-------------------+ +---> +--------------+ <-------+
|listNode *head |----+ |listNode *prev|-->NULL |
+-------------------+ +--------------+ | +--------------+
|listNode *tail |----+ |listNode *next|----+ | +--|listNode *next|
+-------------------+ | +--------------+ | | | +--------------+
|void *(*dup)(...) | | |void *value | | | | |int direction |
+-------------------+ | +--------------+ | | | +--------------+
|void (*free)(...) | | | | |
+-------------------+ | | | |
|int (*match)(...) | | | | |
+-------------------+ +---> +--------------+ <--+ | +-->NULL
|unsigned long len | |listNode *prev|---------+
+-------------------+ +--------------+
|listNode *next|-->NULL
+--------------+
|void *value |
+--------------+
*/
调用next函数的返回值为调用之前的listNode首地址
五、其它操作
redis的双向链表还提供了其它操作。其中,查找指定的key与复制整个list依赖于迭代器的使用,并使用到自定义的比较/复制方法。
除此之外,还提供了类似随机读取的方式,其内部实现为遍历,且“越界”时返回NULL。同时,它支持index为负数,表示从尾开始。类似旋转的操作,把尾节点移至原头节点之前,成为新的头节点。当然,还有拼接两个链表的操作。
redis 5.0.7 下载链接
http://download.redis.io/releases/redis-5.0.7.tar.gz
源码阅读顺序参考:
https://github.com/huangz1990/blog/blob/master/diary/2014/how-to-read-redis-source-code.rst
redis 5.0.7 源码阅读——双向链表的更多相关文章
- redis 5.0.7 源码阅读——整数集合intset
redis中整数集合intset相关的文件为:intset.h与intset.c intset的所有操作与操作一个排序整形数组 int a[N]类似,只是根据类型做了内存上的优化. 一.数据结构 ty ...
- redis 5.0.7 源码阅读——跳跃表skiplist
redis中并没有专门给跳跃表两个文件.在5.0.7的版本中,结构体的声明与定义.接口的声明在server.h中,接口的定义在t_zset.c中,所有开头为zsl的函数. 一.数据结构 单个节点: t ...
- redis 5.0.7 源码阅读——字典dict
redis中字典相关的文件为:dict.h与dict.c 与其说是一个字典,道不如说是一个哈希表. 一.数据结构 dictEntry typedef struct dictEntry { void * ...
- redis 5.0.7 源码阅读——动态字符串sds
redis中动态字符串sds相关的文件为:sds.h与sds.c 一.数据结构 redis中定义了自己的数据类型"sds",用于描述 char*,与一些数据结构 typedef c ...
- redis 5.0.7 源码阅读——压缩列表ziplist
redis中压缩列表ziplist相关的文件为:ziplist.h与ziplist.c 压缩列表是redis专门开发出来为了节约内存的内存编码数据结构.源码中关于压缩列表介绍的注释也写得比较详细. 一 ...
- Linux 0.11源码阅读笔记-文件管理
Linux 0.11源码阅读笔记-文件管理 文件系统 生磁盘 未安装文件系统的磁盘称之为生磁盘,生磁盘也可以作为文件读写,linux中一切皆文件. 磁盘分区 生磁盘可以被分区,分区中可以安装文件系统, ...
- Linux 0.11源码阅读笔记-中断过程
Linux 0.11源码阅读笔记-中断过程 是什么中断 中断发生时,计算机会停止当前运行的程序,转而执行中断处理程序,然后再返回原被中断的程序继续运行.中断包括硬件中断和软件中断,硬中断是由外设自动产 ...
- Linux 0.11源码阅读笔记-总览
Linux 0.11源码阅读笔记-总览 阅读源码的目的 加深对Linux操作系统的了解,了解Linux操作系统基本架构,熟悉进程管理.内存管理等主要模块知识. 通过阅读教复杂的代码,锻炼自己复杂项目代 ...
- redis 4.0.8 源码包安装集群
系统:centos 6.9软件版本:redis-4.0.8,rubygems-2.7.7,gcc version 4.4.7 20120313,openssl-1.1.0h,zlib-1.2.11 y ...
随机推荐
- [ PyQt入门教程 ] PyQt5中多线程模块QThread使用方法
本文主要讲解使用多线程模块QThread解决PyQt界面程序唉执行耗时操作时,程序卡顿出现的无响应以及界面输出无法实时显示的问题.用户使用工具过程中出现这些问题时会误以为程序出错,从而把程序关闭.这样 ...
- linux ftp 服务器 ,web 服务器 配置
ftp服务器配置: 1:vsftpd.conf 2;vsftpd.ftpusers 3:vsftpd.user_list web服务器配置: apache 配置
- Java基础系列2:深入理解String类
Java基础系列2:深入理解String类 String是Java中最为常用的数据类型之一,也是面试中比较常被问到的基础知识点,本篇就聊聊Java中的String.主要包括如下的五个内容: Strin ...
- 基本库使用(urllib,requests)
urllib(request,error,parse,robotparse) request模块 方法:urlopen() {read(),readinto(),getheader(name), ...
- Nginx code 状态码说明
最近了解下Nginx的Code状态码,在此简单总结下. 先来再回顾一下一个http请求处理流程: 一个普通的http请求处理流程,如上图所示: A -> client端发起请求给nginx B ...
- PTA 6-14 用单向链表完成多项式运算(35分)
输入两个多项式,计算它们的加.减及乘法, 将计算结果输出到屏幕上. 1) 输入:从键盘分两行分别输入两个多项式数据,每个多项式输入格式如下: n a1 m1 a2 m2 a3 m3 . .. ai m ...
- Linux下的expect
expect简介 expect是一款自动化的脚本解释型的工具. expect基于tcl脚本,expect脚本的运行需要tcl的支持. expect对一些需要交互输入的命令很有帮助,比如ssh ftp ...
- Spring注解开发系列Ⅵ --- AOP&事务
注解开发 --- AOP AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,横向重复,纵向抽取.详细的AO ...
- openstack 架构
openstack 概念架构: openstack逻辑架构 常见的架构:
- Grevl旅游注册的初步界面,以源代码和运行图片展示
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...