前言
如果说在计算机的工程应用中,总有一些处于核心地位的数据结构,个人认为有以下几个:
- 链表
- 数组
- 红黑树
本文主要讲的是红黑树,红黑树的原理随便用百度一搜都能搜出一大把,但是网上很多博主关于红黑树的代码实现偏少,且这些代码绝大部分以demo的形式提供,代码的执行效率、代码稳定性、扩展性也很难保证,我们在实际上的工程项目也不敢用到这些代码。所以,本文主要所要做的工作是将Linux所使用的代码进行提取、分析、调试,一方面是为了学习、另一方面是为了方便以后将代码应用到真正的工程上去。当然,中间如果涉及到Linux的代码版权问题,请大家自行考虑。
由于Linux内部使用的红黑树,涉及到很多方便,比如部分的优化、缓存、修改(interval tree),将分为多个文章,每次尽量存放源代码,以及demo,包括demo的运行结果。
红黑树所在的源码位置
fh@192 linux-5.12.10 % ls -l
total 1528
-rw-r--r-- 1 fh staff 496 6 10 19:41 COPYING
-rw-r--r-- 1 fh staff 100629 6 10 19:41 CREDITS
drwxr-xr-x 98 fh staff 3136 6 10 19:41 Documentation
-rw-r--r-- 1 fh staff 1327 6 10 19:41 Kbuild
-rw-r--r-- 1 fh staff 555 6 10 19:41 Kconfig
drwxr-xr-x 6 fh staff 192 6 10 19:41 LICENSES
-rw-r--r-- 1 fh staff 591724 6 10 19:41 MAINTAINERS
-rw-r--r-- 1 fh staff 66287 6 10 19:41 Makefile
-rw-r--r-- 1 fh staff 727 6 10 19:41 README
drwxr-xr-x 27 fh staff 864 6 10 19:41 arch
drwxr-xr-x 72 fh staff 2304 6 10 19:41 block
drwxr-xr-x 11 fh staff 352 6 10 19:41 certs
drwxr-xr-x 145 fh staff 4640 6 10 19:41 crypto
drwxr-xr-x 141 fh staff 4512 6 10 19:41 drivers
drwxr-xr-x 157 fh staff 5024 6 10 19:41 fs
drwxr-xr-x 29 fh staff 928 6 10 19:41 include
drwxr-xr-x 14 fh staff 448 6 10 19:41 init
drwxr-xr-x 15 fh staff 480 6 10 19:41 ipc
drwxr-xr-x 133 fh staff 4256 6 10 19:41 kernel
drwxr-xr-x 269 fh staff 8608 6 10 19:41 lib
drwxr-xr-x 122 fh staff 3904 6 10 19:41 mm
drwxr-xr-x 77 fh staff 2464 6 10 19:41 net
drwxr-xr-x 35 fh staff 1120 6 10 19:41 samples
drwxr-xr-x 139 fh staff 4448 6 10 19:41 scripts
drwxr-xr-x 22 fh staff 704 6 10 19:41 security
drwxr-xr-x 31 fh staff 992 6 10 19:41 sound
drwxr-xr-x 38 fh staff 1216 6 10 19:41 tools
drwxr-xr-x 10 fh staff 320 6 10 19:41 usr
drwxr-xr-x 5 fh staff 160 6 10 19:41 virt
fh@192 linux-5.12.10 % ls include/linux/rb*
include/linux/rbtree.h include/linux/rbtree_augmented.h include/linux/rbtree_latch.h
fh@192 linux-5.12.10 % ls lib/rb*
lib/rbtree.c lib/rbtree_test.c
fh@192 linux-5.12.10 %
红黑树,主要包括三个文件(暂时不考虑红黑树的其他相关应用):
- rbtree.h(一般功能的头文件)
- rbtree_augmented.h(扩展功能的头文件)
- rbtree.c (部分的函数实现)
解决编译问题
将这几个文件代码放到使用Makefile或者其他IDE中,就会发现存在符号找不到问题,需要对代码进行适当的修改,使其能够编译过去。
注掉无效的文件包含:
#if 0
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/rcupdate.h>
#endif
增加头文件:
#include <stddef.h>
#include <stdbool.h>
最好修改掉的头文件#ifndef...#endif的名称,尽量避免重复。
增加宏定义:
#if defined(container_of)
#undef container_of
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#else
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#endif
#if defined(offsetof)
#undef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
#undef NULL
#if defined(__cplusplus)
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#define WRITE_ONCE(var, val) \
(*((volatile typeof(val) *)(&(var))) = (val))
#ifndef likely
# define likely(x) __builtin_expect(!!(x), 1)
#endif
#ifndef unlikely
# define unlikely(x) __builtin_expect(!!(x), 0)
#endif
这些宏定义,可以直接使用代码阅读软件找一下然后拷贝过来就行,不难。值得注意的是,WRITE_ONCE和READ_ONCE大家可以直接手动修改,比如可以将WRITE_ONCE改成var = val,这两个宏定义其实也就是为了提升一下性能,增加了volatile部分修饰。
注释掉rcu相关函数,使用#if 0就可以了。
解决掉上面提到的这些,代码基本上可以编译通过了,下面介绍一下用法。
编写测试代码
//
// rbtree_demo.c
// RbTree
//
// Created by ar on 2021/6/11.
//
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "rbtree.h"
#define TST_NODE_NUM (10)
int BP_snprintf(char *pcBuf, unsigned int ulLen, const char *pcFmt, ...)
{
va_list stLi;
va_start(stLi, pcFmt);
int lRet = (int) vsnprintf(pcBuf, ulLen, pcFmt, stLi);
va_end(stLi);
return lRet;
}
typedef struct STU_INFO_S
{
char szName[32];
struct rb_node stNode;
unsigned int ulId;
}STU_INFO_S;
static bool STU_KeyLessCmp(struct rb_node *pstArgNode, const struct rb_node *pstTreeNode)
{
STU_INFO_S *pstArg = rb_entry(pstArgNode, STU_INFO_S, stNode);
STU_INFO_S *pstTreeData = rb_entry(pstTreeNode, STU_INFO_S, stNode);
if (pstArg->ulId <= pstTreeData->ulId)
{
return true;
}
return false;
}
static int STU_KeyCmp(const void *pKey, const struct rb_node *pstNode)
{
unsigned int *pulKey = (unsigned int *)pKey;
STU_INFO_S *pstData = rb_entry(pstNode, STU_INFO_S, stNode);
if (*pulKey == pstData->ulId)
{
return 0;
}
else if (*pulKey < pstData->ulId)
{
return -1;
}
else
{
return 1;
}
return 0;
}
void STU_Demo()
{
struct rb_root stRoot = RB_ROOT;
struct rb_node *pstNode = NULL;
struct STU_INFO_S *pstStu = NULL;
for (unsigned int i = 0; i < TST_NODE_NUM; ++i)
{
pstStu = (struct STU_INFO_S *)malloc(sizeof(struct STU_INFO_S));
memset(pstStu, 0, sizeof(STU_INFO_S));
BP_snprintf(pstStu->szName, 32, "name-%u", i);
pstStu->ulId = i;
rb_add(&(pstStu->stNode), &stRoot, STU_KeyLessCmp);
printf("add, addr: %x, id: %u, name: %s. \n", pstStu, pstStu->ulId, pstStu->szName);
}
if (RB_EMPTY_ROOT(&stRoot))
{
printf("RB tree: empty. \n");
}
else
{
printf("RB tree: not empty. \n");
}
for (unsigned int i = 0; i < TST_NODE_NUM; ++i)
{
struct rb_node *pstNode = rb_find(&i, &stRoot, STU_KeyCmp);
STU_INFO_S *pstData = rb_entry(pstNode, STU_INFO_S, stNode);
printf("Find, Key: %u, Val: %s \n", i, pstData->szName);
}
for (pstNode = rb_first(&stRoot); pstNode; pstNode = rb_next(pstNode))
{
pstStu = rb_entry(pstNode, STU_INFO_S, stNode);
printf("Traverse, id: %u, name: %s. \n", pstStu->ulId, pstStu->szName);
}
/* 查找,非空删除,注意释放的内存地址 */
unsigned int ulKey = TST_NODE_NUM / 2;
pstNode = rb_find(&ulKey, &stRoot, STU_KeyCmp);
if (NULL != pstNode)
{
pstStu = rb_entry_safe(pstNode, STU_INFO_S, stNode);
printf("remove node, addr: %x, id: %u, name: %s. \n", pstStu, pstStu->ulId, pstStu->szName);
free(pstStu);
rb_erase(pstNode, &stRoot);
}
for (pstNode = rb_first(&stRoot); pstNode; pstNode = rb_next(pstNode))
{
pstStu = rb_entry(pstNode, STU_INFO_S, stNode);
printf("Rm confirm, id: %u, name: %s. \n", pstStu->ulId, pstStu->szName);
}
for (pstNode = rb_first(&stRoot); pstNode; pstNode = rb_next(pstNode))
{
pstStu = rb_entry(pstNode, STU_INFO_S, stNode);
rb_erase(pstNode, &stRoot);
printf("remove node, addr: %x, id: %u, name: %s. \n", pstStu, pstStu->ulId, pstStu->szName);
free(pstStu);
}
if (RB_EMPTY_ROOT(&stRoot))
{
printf("RB tree: empty. \n");
}
else
{
printf("RB tree: not empty. \n");
}
}
static void STU_Add(STU_INFO_S *pstStu, struct rb_root *pstRoot)
{
rb_add(&(pstStu->stNode), pstRoot, STU_KeyLessCmp);
printf("add, addr: %x, id: %u, name: %s. \n", pstStu, pstStu->ulId, pstStu->szName);
}
static int STU_RbFindAddCmp(struct rb_node *pstNode1, const struct rb_node *pstNode2)
{
int c = 0;
STU_INFO_S *pstData1 = rb_entry_safe(pstNode1, STU_INFO_S, stNode);
STU_INFO_S *pstData2 = rb_entry_safe(pstNode2, STU_INFO_S, stNode);
if (pstData1->ulId == pstData2->ulId)
{
c = 0;
}
else if (pstData1->ulId < pstData2->ulId)
{
c = -1;
}
else
{
c = 1;
}
return c;
}
void STU_Demo2()
{
struct rb_root stRoot = RB_ROOT;
struct rb_node *pstNode = NULL;
struct STU_INFO_S *pstStu = NULL;
for (unsigned int i = 0; i < TST_NODE_NUM; ++i)
{
pstStu = (struct STU_INFO_S *)malloc(sizeof(struct STU_INFO_S));
memset(pstStu, 0, sizeof(STU_INFO_S));
BP_snprintf(pstStu->szName, 32, "name-%u", i);
pstStu->ulId = i;
STU_Add(pstStu, &stRoot);
}
/* 重复添加 */
for (unsigned int i = 0; i < TST_NODE_NUM; ++i)
{
if (3 == i)
{
for (unsigned int j = 0; j < i; ++j)
{
pstStu = (struct STU_INFO_S *)malloc(sizeof(struct STU_INFO_S));
memset(pstStu, 0, sizeof(STU_INFO_S));
pstStu->ulId = i;
BP_snprintf(pstStu->szName, 32, "cpy-name-%u-%u", i, j);
STU_Add(pstStu, &stRoot);
}
}
}
for (pstNode = rb_first(&stRoot); pstNode; pstNode = rb_next(pstNode))
{
pstStu = rb_entry(pstNode, STU_INFO_S, stNode);
printf("Add info, id: %u, name: %s. \n", pstStu->ulId, pstStu->szName);
}
unsigned int ulKey = 3;
rb_for_each(pstNode, &ulKey, &stRoot, STU_KeyCmp)
{
pstStu = rb_entry_safe(pstNode, STU_INFO_S, stNode);
printf("rb for_each, id: %u, name: %s. \n", pstStu->ulId, pstStu->szName);
}
}
void STU_Demo3()
{
struct rb_root stRoot = RB_ROOT;
struct rb_node *pstNode = NULL;
struct STU_INFO_S *pstStu = NULL;
struct rb_node *pstParent = NULL;
struct STU_INFO_S *pstExist = NULL;
for (unsigned int i = 0; i < TST_NODE_NUM; ++i)
{
pstStu = (struct STU_INFO_S *)malloc(sizeof(struct STU_INFO_S));
memset(pstStu, 0, sizeof(STU_INFO_S));
BP_snprintf(pstStu->szName, 32, "name-%u", i);
pstStu->ulId = i;
pstParent = rb_find_add(&(pstStu->stNode), &stRoot, STU_RbFindAddCmp);
if (NULL == pstParent)
{
/* 不存在,则插入成功*/
printf("Succeed to insert not exist, id: %u, name: %s. \n", pstStu->ulId, pstStu->szName);
}
else
{
/* 插入失败 */
printf("Failed to insert exist, id: %u, name: %s. \n", pstStu->ulId, pstStu->szName);
free(pstStu);
}
}
/* 重复添加 */
for (unsigned int i = 0; i < TST_NODE_NUM; ++i)
{
pstStu = (struct STU_INFO_S *)malloc(sizeof(struct STU_INFO_S));
memset(pstStu, 0, sizeof(STU_INFO_S));
pstStu->ulId = i;
BP_snprintf(pstStu->szName, 32, "cpy-name-%u", i);
pstParent = rb_find_add(&(pstStu->stNode), &stRoot, STU_RbFindAddCmp);
if (NULL == pstParent)
{
/* 不存在,则插入成功*/
printf("Succeed to insert not exist, id: %u, name: %s. \n", pstStu->ulId, pstStu->szName);
}
else
{
/* 已存在, 插入失败 */
pstExist = rb_entry_safe(pstParent, STU_INFO_S, stNode);
printf("Failed to insert exist, id: %u, name: %s, id2: %u, name2: %s. \n", pstStu->ulId, pstStu->szName, pstExist->ulId, pstExist->szName);
free(pstStu);
}
}
for (pstNode = rb_first(&stRoot); pstNode; pstNode = rb_next(pstNode))
{
pstStu = rb_entry(pstNode, STU_INFO_S, stNode);
printf("Add info, id: %u, name: %s. \n", pstStu->ulId, pstStu->szName);
}
}
自己写一个main函数,然后调用里面的STU_Demo函数就可以了
运行结果
STU_Demo的运行结果
add, addr: 6541c0, id: 0, name: name-0.
add, addr: 654200, id: 1, name: name-1.
add, addr: 654240, id: 2, name: name-2.
add, addr: 654280, id: 3, name: name-3.
add, addr: 654500, id: 4, name: name-4.
add, addr: 654540, id: 5, name: name-5.
add, addr: 654580, id: 6, name: name-6.
add, addr: 6545c0, id: 7, name: name-7.
add, addr: 654600, id: 8, name: name-8.
add, addr: 654640, id: 9, name: name-9.
RB tree: not empty.
Find, Key: 0, Val: name-0
Find, Key: 1, Val: name-1
Find, Key: 2, Val: name-2
Find, Key: 3, Val: name-3
Find, Key: 4, Val: name-4
Find, Key: 5, Val: name-5
Find, Key: 6, Val: name-6
Find, Key: 7, Val: name-7
Find, Key: 8, Val: name-8
Find, Key: 9, Val: name-9
Traverse, id: 0, name: name-0.
Traverse, id: 1, name: name-1.
Traverse, id: 2, name: name-2.
Traverse, id: 3, name: name-3.
Traverse, id: 4, name: name-4.
Traverse, id: 5, name: name-5.
Traverse, id: 6, name: name-6.
Traverse, id: 7, name: name-7.
Traverse, id: 8, name: name-8.
Traverse, id: 9, name: name-9.
remove node, addr: 654540, id: 5, name: name-5.
Rm confirm, id: 0, name: name-0.
Rm confirm, id: 1, name: name-1.
Rm confirm, id: 2, name: name-2.
Rm confirm, id: 3, name: name-3.
Rm confirm, id: 4, name: name-4.
Rm confirm, id: 6, name: name-6.
Rm confirm, id: 7, name: name-7.
Rm confirm, id: 8, name: name-8.
Rm confirm, id: 9, name: name-9.
remove node, addr: 6541c0, id: 0, name: name-0.
remove node, addr: 654200, id: 1, name: name-1.
remove node, addr: 654240, id: 2, name: name-2.
remove node, addr: 654280, id: 3, name: name-3.
remove node, addr: 654500, id: 4, name: name-4.
remove node, addr: 654580, id: 6, name: name-6.
remove node, addr: 6545c0, id: 7, name: name-7.
remove node, addr: 654600, id: 8, name: name-8.
remove node, addr: 654640, id: 9, name: name-9.
RB tree: empty.
STU_Demo2的运行结果
add, addr: 5c28dd0, id: 0, name: name-0.
add, addr: 5c28e10, id: 1, name: name-1.
add, addr: 5c28e50, id: 2, name: name-2.
add, addr: 5c28e90, id: 3, name: name-3.
add, addr: 5c292d0, id: 4, name: name-4.
add, addr: 5c29310, id: 5, name: name-5.
add, addr: 5c29350, id: 6, name: name-6.
add, addr: 5c29390, id: 7, name: name-7.
add, addr: 5c293d0, id: 8, name: name-8.
add, addr: 5c29410, id: 9, name: name-9.
add, addr: 5c29450, id: 3, name: cpy-name-3-0.
add, addr: 5c29490, id: 3, name: cpy-name-3-1.
add, addr: 5c294d0, id: 3, name: cpy-name-3-2.
Add info, id: 0, name: name-0.
Add info, id: 1, name: name-1.
Add info, id: 2, name: name-2.
Add info, id: 3, name: cpy-name-3-2.
Add info, id: 3, name: cpy-name-3-1.
Add info, id: 3, name: cpy-name-3-0.
Add info, id: 3, name: name-3.
Add info, id: 4, name: name-4.
Add info, id: 5, name: name-5.
Add info, id: 6, name: name-6.
Add info, id: 7, name: name-7.
Add info, id: 8, name: name-8.
Add info, id: 9, name: name-9.
rb for_each, id: 3, name: cpy-name-3-2.
rb for_each, id: 3, name: cpy-name-3-1.
rb for_each, id: 3, name: cpy-name-3-0.
rb for_each, id: 3, name: name-3.
STU_Demo3的运行结果
Succeed to insert not exist, id: 0, name: name-0.
Succeed to insert not exist, id: 1, name: name-1.
Succeed to insert not exist, id: 2, name: name-2.
Succeed to insert not exist, id: 3, name: name-3.
Succeed to insert not exist, id: 4, name: name-4.
Succeed to insert not exist, id: 5, name: name-5.
Succeed to insert not exist, id: 6, name: name-6.
Succeed to insert not exist, id: 7, name: name-7.
Succeed to insert not exist, id: 8, name: name-8.
Succeed to insert not exist, id: 9, name: name-9.
Failed to insert exist, id: 0, name: cpy-name-0, id2: 0, name2: name-0.
Failed to insert exist, id: 1, name: cpy-name-1, id2: 1, name2: name-1.
Failed to insert exist, id: 2, name: cpy-name-2, id2: 2, name2: name-2.
Failed to insert exist, id: 3, name: cpy-name-3, id2: 3, name2: name-3.
Failed to insert exist, id: 4, name: cpy-name-4, id2: 4, name2: name-4.
Failed to insert exist, id: 5, name: cpy-name-5, id2: 5, name2: name-5.
Failed to insert exist, id: 6, name: cpy-name-6, id2: 6, name2: name-6.
Failed to insert exist, id: 7, name: cpy-name-7, id2: 7, name2: name-7.
Failed to insert exist, id: 8, name: cpy-name-8, id2: 8, name2: name-8.
Failed to insert exist, id: 9, name: cpy-name-9, id2: 9, name2: name-9.
Add info, id: 0, name: name-0.
Add info, id: 1, name: name-1.
Add info, id: 2, name: name-2.
Add info, id: 3, name: name-3.
Add info, id: 4, name: name-4.
Add info, id: 5, name: name-5.
Add info, id: 6, name: name-6.
Add info, id: 7, name: name-7.
Add info, id: 8, name: name-8.
Add info, id: 9, name: name-9.
以上均可以看出红黑树的增/删/查,修改暂时就不管了,因为只要你查到指针,直接就可以修改了。