当前位置: 首页 > news >正文

Linux中基数树的初始化

初始化基数树radix_tree_init

void __init radix_tree_init(void)
{radix_tree_node_cachep = kmem_cache_create("radix_tree_node",sizeof(struct radix_tree_node), 0,SLAB_PANIC, radix_tree_node_ctor, NULL);radix_tree_init_maxindex();hotcpu_notifier(radix_tree_callback, 0);
}
static void
radix_tree_node_ctor(void *node, kmem_cache_t *cachep, unsigned long flags)
{memset(node, 0, sizeof(struct radix_tree_node));
}

函数整体功能

这个函数用于初始化 Linux 内核中的基数树(radix tree)子系统,创建内存缓存池、初始化最大索引值,并注册 CPU 热插拔通知器

代码分段详解

第一段:创建内存缓存池

radix_tree_node_cachep = kmem_cache_create("radix_tree_node",sizeof(struct radix_tree_node), 0,SLAB_PANIC, radix_tree_node_ctor, NULL);

详细解释:

  • kmem_cache_create(): 内核函数,创建专用的内存对象缓存池
  • "radix_tree_node": 缓存池的名称,用于调试和识别
  • sizeof(struct radix_tree_node): 每个对象的大小,即基数树节点的大小
  • 0: 对齐参数,0表示使用默认对齐
  • SLAB_PANIC: 标志位,如果内存分配失败则系统panic
  • radix_tree_node_ctor: 构造函数指针,创建对象时自动调用
  • NULL: 析构函数,这里不需要
  • radix_tree_node_cachep: 全局变量,保存创建的缓存池指针

作用: 为基数树节点创建专用的内存缓存,提高内存分配效率。

第二段:初始化最大索引值

radix_tree_init_maxindex();

详细解释:

  • 调用 radix_tree_init_maxindex() 函数
  • 该函数内部会计算并设置每个高度(level)能存储的最大索引值
  • 根据树的深度和每个节点的槽位数(RADIX_TREE_MAP_SIZE)计算
  • 结果存储在全局数组 height_to_maxindex[]

作用: 预计算不同树高度对应的最大键值,用于后续的边界检查。

第三段:注册CPU热插拔通知

hotcpu_notifier(radix_tree_callback, 0);

详细解释:

  • hotcpu_notifier(): 注册CPU热插拔事件的通知器
  • radix_tree_callback: 回调函数,当CPU状态变化时被调用
  • 0: 优先级,0表示默认优先级

作用: 确保在多CPU环境下,当CPU被热添加或移除时,基数树能够正确地进行内存管理和状态同步

构造函数详解

static void
radix_tree_node_ctor(void *node, kmem_cache_t *cachep, unsigned long flags)
{memset(node, 0, sizeof(struct radix_tree_node));
}

详细解释:

  • radix_tree_node_ctor: 构造函数名称
  • void *node: 新分配的内存对象指针
  • kmem_cache_t *cachep: 所属的缓存池
  • unsigned long flags: 分配标志
  • memset(node, 0, sizeof(struct radix_tree_node)): 将新节点内存清零

作用: 确保每个新分配的基数树节点初始状态为全零,避免未初始化内存带来的问题

预计算基数树在不同高度的最大索引值radix_tree_init_maxindex

static __init unsigned long __maxindex(unsigned int height)
{unsigned int tmp = height * RADIX_TREE_MAP_SHIFT;unsigned long index = (~0UL >> (RADIX_TREE_INDEX_BITS - tmp - 1)) >> 1;if (tmp >= RADIX_TREE_INDEX_BITS)index = ~0UL;return index;
}
static __init void radix_tree_init_maxindex(void)
{unsigned int i;for (i = 0; i < ARRAY_SIZE(height_to_maxindex); i++)height_to_maxindex[i] = __maxindex(i);
}

函数整体功能

这两个函数用于预计算基数树在不同高度(level)下能够存储的最大索引值,并将结果存储在全局数组中供后续使用

__maxindex 函数详解

函数定义

static __init unsigned long __maxindex(unsigned int height)
  • static: 限制函数作用域在当前文件
  • __init: 表示这是初始化函数,内核启动后会被释放
  • unsigned long: 返回值类型,64位系统上是64位无符号整数
  • height: 参数,表示基数树的高度

第一段:计算位移量

unsigned int tmp = height * RADIX_TREE_MAP_SHIFT;

详细解释:

  • RADIX_TREE_MAP_SHIFT: 通常为6(因为 2^6 = 64,每个节点有64个槽位)
  • height: 树的高度,从0开始
  • tmp: 计算该高度下总共可以表示的比特位数
  • 示例: 如果高度为2,则 tmp = 2 * 6 = 12,表示可以索引 2^12 = 4096 个元素

第二段:计算最大索引值

unsigned long index = (~0UL >> (RADIX_TREE_INDEX_BITS - tmp - 1)) >> 1;

这是最复杂的部分,分解解释:

  1. ~0UL:

    • 将所有位设为1的unsigned long值
  2. RADIX_TREE_INDEX_BITS:

    • 通常32(在32位系统上)
    • 表示索引的最大比特位数
  3. (RADIX_TREE_INDEX_BITS - tmp - 1):

    • 计算需要右移的位数
    • 减1是为了避免溢出
  4. (~0UL >> (RADIX_TREE_INDEX_BITS - tmp - 1)):

    • 通过右移创建一个掩码
    • 示例: 如果tmp=12,则右移 32-12-1 = 19位
  5. >> 1:

    • 再右移1位,确保索引值正确

实际效果: 创建一个具有 tmp 个有效位的最大数值,计算在给定高度下,基数树可以寻址的总比特位数

为什么不直接通过(1 << tmp) - 1实现?

  • 原因:当tmp=32时,1 << 32 在32位系统上会溢出(因为只能左移0-31位),在64为系统上同理

为什么要-1>>1,不能直接右移(RADIX_TREE_INDEX_BITS - tmp)吗?

  • 原因:当 tmp=0 时,我们想要最大索引为0(因为高度为0不能存储任何元素),如果直接用 ~0UL >> (64 - 0)是未定义行为

第三段:边界检查

if (tmp >= RADIX_TREE_INDEX_BITS)index = ~0UL;

详细解释:

  • 如果计算的比特位数 tmp 大于等于最大索引比特数
  • 说明这个高度可以表示所有可能的索引值
  • 直接将最大索引设为全1(最大值)
  • 防止情况: 当高度足够大时,避免计算错误

radix_tree_init_maxindex 函数详解

函数定义

static __init void radix_tree_init_maxindex(void)
  • void: 无返回值
  • 初始化函数,在内核启动时调用

循环初始化数组

unsigned int i;for (i = 0; i < ARRAY_SIZE(height_to_maxindex); i++)height_to_maxindex[i] = __maxindex(i);

详细解释:

  • height_to_maxindex[]: 全局数组,存储每个高度对应的最大索引
  • ARRAY_SIZE(): 宏,计算数组大小
  • 循环遍历所有可能的高度
  • 对每个高度 i,调用 __maxindex(i) 计算最大索引值
  • 将结果存储在全局数组的对应位置

清理基数树预分配节点缓存radix_tree_callback

#ifdef CONFIG_HOTPLUG_CPU
static int radix_tree_callback(struct notifier_block *nfb,unsigned long action,void *hcpu)
{int cpu = (long)hcpu;struct radix_tree_preload *rtp;/* Free per-cpu pool of perloaded nodes */if (action == CPU_DEAD) {rtp = &per_cpu(radix_tree_preloads, cpu);while (rtp->nr) {kmem_cache_free(radix_tree_node_cachep,rtp->nodes[rtp->nr-1]);rtp->nodes[rtp->nr-1] = NULL;rtp->nr--;}}return NOTIFY_OK;
}
#endif /* CONFIG_HOTPLUG_CPU */

函数整体功能

这是一个CPU热插拔回调函数,当CPU被移除时,负责清理该CPU的基数树预分配节点缓存,防止内存泄漏。、

代码分段详解

第一段:条件编译和函数定义

#ifdef CONFIG_HOTPLUG_CPU
static int radix_tree_callback(struct notifier_block *nfb,unsigned long action,void *hcpu)

详细解释:

  • #ifdef CONFIG_HOTPLUG_CPU: 条件编译,只有在内核配置支持CPU热插拔时才编译此代码
  • static int: 静态函数,返回整型状态码
  • struct notifier_block *nfb: 通知块指针,标识哪个通知器被调用
  • unsigned long action: 动作类型,表示发生了什么事件(CPU上线、下线等)
  • void *hcpu: 泛型指针,指向相关的CPU编号

第二段:获取CPU编号

{int cpu = (long)hcpu;

详细解释:

  • int cpu = (long)hcpu: 将泛型指针转换为long类型,再赋给cpu变量
  • hcpu 参数实际上包含的是CPU的编号
  • 这种转换是Linux内核通知链的标准做法
  • 作用: 获取事件相关的具体CPU编号

第三段:声明变量

       struct radix_tree_preload *rtp;

详细解释:

  • struct radix_tree_preload *rtp: 声明指向基数树预加载结构的指针
  • radix_tree_preload 结构通常包含预分配节点数组和计数
  • 这个结构是per-CPU的,每个CPU有自己的实例

第四段:检查CPU死亡事件

       /* Free per-cpu pool of perloaded nodes */if (action == CPU_DEAD) {

详细解释:

  • 释放每个CPU的预加载节点池
  • action == CPU_DEAD: 检查是否是CPU死亡(被移除)事件
  • CPU_DEAD 是预定义常量,表示CPU已经完全下线
  • 作用: 只处理CPU移除事件,忽略其他事件(如CPU上线)

第五段:获取per-CPU数据

               rtp = &per_cpu(radix_tree_preloads, cpu);

详细解释:

  • per_cpu(radix_tree_preloads, cpu): 获取指定CPU的预加载数据结构
  • radix_tree_preloads: 全局per-CPU变量名
  • cpu: 目标CPU编号
  • &: 取地址,得到指向该CPU数据结构的指针
  • 作用: 定位到要被移除的CPU的专用数据结构

第六段:循环释放预分配节点

               while (rtp->nr) {kmem_cache_free(radix_tree_node_cachep,rtp->nodes[rtp->nr-1]);rtp->nodes[rtp->nr-1] = NULL;rtp->nr--;}

第一行:循环条件

while (rtp->nr) {
  • rtp->nr: 预分配节点的数量计数器
  • 循环继续条件:当还有预分配节点时(nr > 0)

第二行:释放节点内存

kmem_cache_free(radix_tree_node_cachep, rtp->nodes[rtp->nr-1]);
  • kmem_cache_free(): 释放对象回缓存池
  • radix_tree_node_cachep: 全局基数树节点缓存池
  • rtp->nodes[rtp->nr-1]: 节点数组的最后一个元素(因为nr是计数,索引从0开始)
  • 作用: 将节点内存归还给缓存池

第三行:清空指针

rtp->nodes[rtp->nr-1] = NULL;
  • 将数组中的指针设为NULL,防止悬空指针
  • 这是良好的编程实践,避免后续误用

第四行:递减计数器

rtp->nr--;
  • 减少预分配节点计数
  • 为处理下一个节点做准备

第七段:返回成功状态

       }return NOTIFY_OK;
}
#endif /* CONFIG_HOTPLUG_CPU */

详细解释:

  • return NOTIFY_OK: 返回通知处理成功状态
  • NOTIFY_OK 是预定义常量,表示通知已正确处理
  • #endif: 结束条件编译块

数据结构关系图

CPU0
预加载结构 rtp0
CPU1
预加载结构 rtp1
CPU2
预加载结构 rtp2
节点数组 nodes
节点0
节点1
节点nr-1
基数树节点缓存池
http://www.dtcms.com/a/511731.html

相关文章:

  • 4.3 二维数组
  • 【C语言实战(40)】C语言查找算法:从基础到实战的效率进阶
  • 洛谷 P2949 [USACO09OPEN] Work Scheduling G
  • 建站公司杭州南宁制作网站服务商
  • Deepseek-ocr论文精读
  • 【完整源码+数据集+部署教程】【文件&发票】发票信息提取系统源码&数据集全套:改进yolo11-ContextGuided
  • SpringBoot+Shiro+mybatis教务管理系统源码
  • 佛山个人制作网站公司手机百度下载免费安装
  • Git 项目开发核心指南:聚焦常用语法与完整流程
  • 【图像处理基石】遥感多光谱图像处理入门:从概念到实战(附Python代码)
  • Spring Boot项目中使用线程池并发插入6万条数据的线程池参数设置指南
  • 网站建设网站设计哪家专业东莞展馆设计公司
  • Docker Swarm:打造高效、可扩展的容器编排引擎,引领微服务新纪元(上)
  • 第15章:Spring AI Alibaba — 认识Graph框架
  • [Dify 实战] 构建一个自动发送邮件的插件:从 OpenAPI 到自动化通知
  • 基于Chrome140的FB账号自动化(关键词浏览)——脚本撰写(二)
  • CICD实战(8) - 使用Arbess+GitLab实现React.js项目自动化部署
  • 小程序uview actionSheet 内容过多高度设置
  • 基于.net的个人网站开发实录哪个网站建站比较好
  • 徐州做网站公司哪家好湘建网
  • 做头发个人网站制作素材专业网站设计制作服务
  • Linux初识进程
  • c#using Oracle.ManagedDataAccess.Client 批量保存数据
  • 人大金仓数据库kingbase8创建表示例
  • oracle包编译错误
  • 函数指针 指针函数 数组指针 指针数组 常量指针 指针常量
  • sqoop采集完成后导致hdfs数据与Oracle数据量不符的问题。怎么解决?
  • 洛阳有做网站开发的吗平台网站建设源码
  • 从零开始的C++学习生活 12:AVL树全面解析
  • Spring Boot 启动慢?启动过程深度解析与优化策略