Linux红黑树源码调试与分析 <一>

前言

如果说在计算机的工程应用中,总有一些处于核心地位的数据结构,个人认为有以下几个:

  • 链表
  • 数组
  • 红黑树

本文主要讲的是红黑树,红黑树的原理随便用百度一搜都能搜出一大把,但是网上很多博主关于红黑树的代码实现偏少,且这些代码绝大部分以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. 

以上均可以看出红黑树的增/删/查,修改暂时就不管了,因为只要你查到指针,直接就可以修改了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值