Linux中基数树批量查询数据项相关函数的实现

查找从指定索引开始的多个数据项__lookup

c 复制代码
static unsigned int
__lookup(struct radix_tree_root *root, void **results, unsigned long index,
        unsigned int max_items, unsigned long *next_index)
{
        unsigned int nr_found = 0;
        unsigned int shift;
        unsigned int height = root->height;
        struct radix_tree_node *slot;

        shift = (height-1) * RADIX_TREE_MAP_SHIFT;
        slot = root->rnode;

        while (height > 0) {
                unsigned long i = (index >> shift) & RADIX_TREE_MAP_MASK;

                for ( ; i < RADIX_TREE_MAP_SIZE; i++) {
                        if (slot->slots[i] != NULL)
                                break;
                        index &= ~((1UL << shift) - 1);
                        index += 1UL << shift;
                        if (index == 0)
                                goto out;       /* 32-bit wraparound */
                }
                if (i == RADIX_TREE_MAP_SIZE)
                        goto out;
                height--;
                if (height == 0) {      /* Bottom level: grab some items */
                        unsigned long j = index & RADIX_TREE_MAP_MASK;

                        for ( ; j < RADIX_TREE_MAP_SIZE; j++) {
                                index++;
                                if (slot->slots[j]) {
                                        results[nr_found++] = slot->slots[j];
                                        if (nr_found == max_items)
                                                goto out;
                                }
                        }
                }
                shift -= RADIX_TREE_MAP_SHIFT;
                slot = slot->slots[i];
        }
out:
        *next_index = index;
        return nr_found;
}

函数整体功能

这个函数用于从基数树中查找从指定索引开始的多个数据项,支持范围查找和批量获取

代码分段详解

第一段:函数定义和变量声明

c 复制代码
static unsigned int
__lookup(struct radix_tree_root *root, void **results, unsigned long index,
        unsigned int max_items, unsigned long *next_index)
{
        unsigned int nr_found = 0;
        unsigned int shift;
        unsigned int height = root->height;
        struct radix_tree_node *slot;
  • static unsigned int: 静态函数,返回找到的项目数量
  • root: 基数树的根节点指针
  • results: 结果数组,用于存储找到的数据项指针
  • index: 起始查找索引
  • max_items: 最大返回项目数量
  • next_index: 输出参数,下一次查找的起始索引
  • nr_found = 0: 找到的项目计数器
  • shift: 位偏移量,用于计算路径
  • height = root->height: 获取树的高度
  • slot: 当前节点指针

第二段:初始化位移量和起始位置

c 复制代码
        shift = (height-1) * RADIX_TREE_MAP_SHIFT;
        slot = root->rnode;
  • shift = (height-1) * RADIX_TREE_MAP_SHIFT: 计算初始位移量
  • slot = root->rnode: 从根节点开始

第三段:向下遍历树的循环

c 复制代码
        while (height > 0) {
                unsigned long i = (index >> shift) & RADIX_TREE_MAP_MASK;
  • while (height > 0): 循环直到到达叶子层
  • i = (index >> shift) & RADIX_TREE_MAP_MASK: 计算当前层起始槽位

第四段:在当前层查找第一个非空槽位

c 复制代码
                for ( ; i < RADIX_TREE_MAP_SIZE; i++) {
                        if (slot->slots[i] != NULL)
                                break;
                        index &= ~((1UL << shift) - 1);
                        index += 1UL << shift;
                        if (index == 0)
                                goto out;       /* 32-bit wraparound */
                }

循环查找:

c 复制代码
for ( ; i < RADIX_TREE_MAP_SIZE; i++)
  • 从当前位置i开始,遍历当前节点的所有槽位

检查槽位是否非空:

c 复制代码
if (slot->slots[i] != NULL)
    break;
  • 如果找到非空槽位,跳出循环

更新索引:

c 复制代码
index &= ~((1UL << shift) - 1);
index += 1UL << shift;
  • ~((1UL << shift) - 1): 创建掩码,清除低shift位
  • index &= ...: 将索引对齐到当前层的边界
  • index += 1UL << shift: 跳到当前层的下一个段
text 复制代码
初始索引: 0x1234 = 0001 0010 0011 0100 (二进制)

步骤1: 1UL << 12 = 4096 = 0001 0000 0000 0000
步骤2: 4096 - 1 = 4095 = 0000 1111 1111 1111  
步骤3: ~4095 = 1111 0000 0000 0000 (掩码)
步骤4: index & 掩码 = 0x1234 & 0xF000 = 0x1000
步骤5: index + 4096 = 0x1000 + 0x1000 = 0x2000

结果: 从 0x1234 跳到 0x2000

32位回绕检查:

c 复制代码
if (index == 0)
    goto out;       /* 32-bit wraparound */
  • 如果索引回绕到0,说明已经遍历完所有可能索引

第五段:检查是否找到有效路径

c 复制代码
                if (i == RADIX_TREE_MAP_SIZE)
                        goto out;
  • i == RADIX_TREE_MAP_SIZE: 如果遍历完所有槽位都没找到非空槽位
  • goto out: 跳转到函数结尾,返回已找到的项目

第六段:下降一层并检查是否到达叶子层

c 复制代码
                height--;
                if (height == 0) {      /* Bottom level: grab some items */
                        unsigned long j = index & RADIX_TREE_MAP_MASK;

                        for ( ; j < RADIX_TREE_MAP_SIZE; j++) {
                                index++;
                                if (slot->slots[j]) {
                                        results[nr_found++] = slot->slots[j];
                                        if (nr_found == max_items)
                                                goto out;
                                }
                        }
                }

高度递减:

c 复制代码
height--;
  • 下降一层

检查是否到达叶子层:

c 复制代码
if (height == 0)

计算叶子层起始位置:

c 复制代码
unsigned long j = index & RADIX_TREE_MAP_MASK;
  • 获取在叶子节点中的槽位偏移量

遍历叶子节点收集数据:

c 复制代码
for ( ; j < RADIX_TREE_MAP_SIZE; j++) {
    index++;
    if (slot->slots[j]) {
        results[nr_found++] = slot->slots[j];
        if (nr_found == max_items)
            goto out;
    }
}
  • 遍历叶子节点的槽位
  • index++: 每次循环索引加1
  • if (slot->slots[j]): 如果槽位有数据
  • results[nr_found++] = ...: 存储到结果数组,计数器加1
  • if (nr_found == max_items): 如果达到最大数量,退出

第七段:更新位移量和移动到下一节点

c 复制代码
                shift -= RADIX_TREE_MAP_SHIFT;
                slot = slot->slots[i];
        }
  • shift -= RADIX_TREE_MAP_SHIFT: 减少位移量
  • slot = slot->slots[i]: 移动到下一层的节点

第八段:设置下一次查找索引并返回

c 复制代码
out:
        *next_index = index;
        return nr_found;
}
  • out:: 标签,用于goto跳转
  • *next_index = index: 设置下一次查找的起始索引
  • return nr_found: 返回找到的项目数量

关键设计要点

1. 范围查找能力

  • 从指定索引开始查找
  • 支持批量获取多个项目
  • 返回下一次查找的起始位置

2. 稀疏数据处理

  • 跳过空槽位,提高效率
  • 自动对齐到有效数据边界

3. 索引更新逻辑

c 复制代码
index &= ~((1UL << shift) - 1);
index += 1UL << shift;

这段代码确保当在当前层找不到数据时,索引跳到下一个段:

  • 比如在shift=12时,每次跳4096个索引
  • 避免逐个索引检查

4. 性能优化

  • 在中间层快速跳过空子树
  • 在叶子层批量收集数据
  • 最小化不必要的遍历

跨节点的连续批量查找radix_tree_gang_lookup

c 复制代码
unsigned int
radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
                        unsigned long first_index, unsigned int max_items)
{
        const unsigned long max_index = radix_tree_maxindex(root->height);
        unsigned long cur_index = first_index;
        unsigned int ret = 0;

        while (ret < max_items) {
                unsigned int nr_found;
                unsigned long next_index;       /* Index of next search */

                if (cur_index > max_index)
                        break;
                nr_found = __lookup(root, results + ret, cur_index,
                                        max_items - ret, &next_index);
                ret += nr_found;
                if (next_index == 0)
                        break;
                cur_index = next_index;
        }
        return ret;
}

函数整体功能

这个函数是基数树的主要批量查找接口,通过多次调用 __lookup 来实现跨节点的连续批量查找,克服了单个 __lookup 只能查找一个节点的限制

代码分段详解

第一段:函数定义和变量声明

c 复制代码
unsigned int
radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
                        unsigned long first_index, unsigned int max_items)
{
        const unsigned long max_index = radix_tree_maxindex(root->height);
        unsigned long cur_index = first_index;
        unsigned int ret = 0;
  • unsigned int: 返回找到的总项目数量
  • root: 基数树的根节点指针
  • results: 结果数组,存储找到的所有数据项指针
  • first_index: 起始查找索引
  • max_items: 最大返回项目数量
  • max_index = radix_tree_maxindex(root->height): 计算树能容纳的最大索引值
  • cur_index = first_index: 当前查找索引,初始化为起始索引
  • ret = 0: 找到的项目总数计数器,初始为0

第二段:主循环 - 批量查找核心

c 复制代码
        while (ret < max_items) {
  • while (ret < max_items): 主循环条件,当已找到的项目数小于目标数量时继续查找
  • 这个循环确保会跨多个节点进行连续查找,直到找到足够数量或遍历完所有数据

第三段:检查索引范围

c 复制代码
                if (cur_index > max_index)
                        break;
  • if (cur_index > max_index): 检查当前索引是否超出树的最大容量
  • break: 如果超出范围,跳出循环停止查找
  • 作用: 防止无效的查找操作

第四段:调用底层查找函数

c 复制代码
                nr_found = __lookup(root, results + ret, cur_index,
                                        max_items - ret, &next_index);

参数分解:

  • root: 基数树根节点
  • results + ret: 结果数组的当前位置指针
    • ret 是已找到的项目数
    • results + ret 指向结果数组中下一个空闲位置
  • cur_index: 本次查找的起始索引
  • max_items - ret: 本次查找的最大项目数(剩余需要的数量)
  • &next_index: 输出参数,接收下一次查找的起始索引

作用:cur_index 开始查找最多 max_items - ret 个项目,结果存入 results 数组的剩余空间。

第五段:更新找到的项目计数

c 复制代码
                ret += nr_found;
  • ret += nr_found: 将本次找到的项目数累加到总计数中

第六段:检查查找终止条件

c 复制代码
                if (next_index == 0)
                        break;
  • if (next_index == 0): 检查下一次查找索引是否为0
  • break: 如果为0,跳出循环停止查找
  • 为什么 next_index 为0表示结束?
    • 在32位系统中,索引从最大值回绕到0表示已遍历完所有索引
    • __lookup 中遇到这种情况会设置 next_index = 0

第七段:更新下一次查找索引

c 复制代码
                cur_index = next_index;
        }
  • cur_index = next_index: 将当前索引更新为下一次查找的起始位置
  • 循环继续,从新的位置开始下一轮查找

第八段:返回总项目数

c 复制代码
        return ret;
}
  • return ret: 返回最终找到的项目总数
  • 可能小于 max_items(如果树中没有足够的数据项)

查找从指定索引开始具有特定标签的多个数据项__lookup_tag

c 复制代码
static unsigned int
__lookup_tag(struct radix_tree_root *root, void **results, unsigned long index,
        unsigned int max_items, unsigned long *next_index, int tag)
{
        unsigned int nr_found = 0;
        unsigned int shift;
        unsigned int height = root->height;
        struct radix_tree_node *slot;

        shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
        slot = root->rnode;

        while (height > 0) {
                unsigned long i = (index >> shift) & RADIX_TREE_MAP_MASK;

                for ( ; i < RADIX_TREE_MAP_SIZE; i++) {
                        if (tag_get(slot, tag, i)) {
                                BUG_ON(slot->slots[i] == NULL);
                                break;
                        }
                        index &= ~((1UL << shift) - 1);
                        index += 1UL << shift;
                        if (index == 0)
                                goto out;       /* 32-bit wraparound */
                }
                if (i == RADIX_TREE_MAP_SIZE)
                        goto out;
                height--;
                if (height == 0) {      /* Bottom level: grab some items */
                        unsigned long j = index & RADIX_TREE_MAP_MASK;

                        for ( ; j < RADIX_TREE_MAP_SIZE; j++) {
                                index++;
                                if (tag_get(slot, tag, j)) {
                                        BUG_ON(slot->slots[j] == NULL);
                                        results[nr_found++] = slot->slots[j];
                                        if (nr_found == max_items)
                                                goto out;
                                }
                        }
                }
                shift -= RADIX_TREE_MAP_SHIFT;
                slot = slot->slots[i];
        }
out:
        *next_index = index;
        return nr_found;
}

函数整体功能

这个函数用于在基数树中查找从指定索引开始的、具有特定标签的多个数据项,是标签系统的核心查找功能

代码分段详解

第一段:函数定义和变量声明

c 复制代码
static unsigned int
__lookup_tag(struct radix_tree_root *root, void **results, unsigned long index,
        unsigned int max_items, unsigned long *next_index, int tag)
{
        unsigned int nr_found = 0;
        unsigned int shift;
        unsigned int height = root->height;
        struct radix_tree_node *slot;

详细解释:

  • static unsigned int: 静态函数,返回找到的项目数量
  • root: 基数树的根节点指针
  • results: 结果数组,存储找到的数据项指针
  • index: 起始查找索引
  • max_items: 最大返回项目数量
  • next_index: 输出参数,下一次查找的起始索引
  • tag: 要查找的标签编号
  • nr_found = 0: 找到的项目计数器
  • shift: 位偏移量,用于计算路径
  • height = root->height: 获取树的高度
  • slot: 当前节点指针

第二段:初始化位移量和起始位置

c 复制代码
        shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
        slot = root->rnode;
  • shift = (height - 1) * RADIX_TREE_MAP_SHIFT: 计算初始位移量
  • slot = root->rnode: 从根节点开始查找

第三段:向下遍历树的循环

c 复制代码
        while (height > 0) {
                unsigned long i = (index >> shift) & RADIX_TREE_MAP_MASK;
  • while (height > 0): 循环直到到达叶子层
  • i = (index >> shift) & RADIX_TREE_MAP_MASK: 计算当前层起始槽位

第四段:在当前层查找第一个带标签的槽位

c 复制代码
                for ( ; i < RADIX_TREE_MAP_SIZE; i++) {
                        if (tag_get(slot, tag, i)) {
                                BUG_ON(slot->slots[i] == NULL);
                                break;
                        }
                        index &= ~((1UL << shift) - 1);
                        index += 1UL << shift;
                        if (index == 0)
                                goto out;       /* 32-bit wraparound */
                }

循环查找带标签的槽位:

c 复制代码
for ( ; i < RADIX_TREE_MAP_SIZE; i++)
  • 从当前位置i开始,遍历当前节点的所有槽位

检查标签状态:

c 复制代码
if (tag_get(slot, tag, i))
  • 检查当前槽位是否设置了指定标签

调试断言:

c 复制代码
BUG_ON(slot->slots[i] == NULL);
  • 如果槽位有标签但对应的指针为空,触发内核BUG
  • 确保标签系统的数据一致性

跳过空标签段:

c 复制代码
index &= ~((1UL << shift) - 1);
index += 1UL << shift;
  • 如果没有找到带标签的槽位,跳到当前层的下一个段

32位回绕检查:

c 复制代码
if (index == 0)
    goto out;
  • 防止32位系统索引回绕

第五段:检查是否找到有效路径

c 复制代码
                if (i == RADIX_TREE_MAP_SIZE)
                        goto out;
  • i == RADIX_TREE_MAP_SIZE: 如果遍历完所有槽位都没找到带标签的槽位
  • goto out: 跳转到函数结尾

第六段:下降一层并检查是否到达叶子层

c 复制代码
                height--;
                if (height == 0) {      /* Bottom level: grab some items */
                        unsigned long j = index & RADIX_TREE_MAP_MASK;

                        for ( ; j < RADIX_TREE_MAP_SIZE; j++) {
                                index++;
                                if (tag_get(slot, tag, j)) {
                                        BUG_ON(slot->slots[j] == NULL);
                                        results[nr_found++] = slot->slots[j];
                                        if (nr_found == max_items)
                                                goto out;
                                }
                        }
                }

高度递减:

c 复制代码
height--;
  • 下降一层

到达叶子层的处理:

c 复制代码
if (height == 0)

计算叶子层起始位置:

c 复制代码
unsigned long j = index & RADIX_TREE_MAP_MASK;
  • 获取在叶子节点中的槽位偏移量

遍历叶子节点收集带标签的数据:

c 复制代码
for ( ; j < RADIX_TREE_MAP_SIZE; j++) {
    index++;
    if (tag_get(slot, tag, j)) {
        BUG_ON(slot->slots[j] == NULL);
        results[nr_found++] = slot->slots[j];
        if (nr_found == max_items)
            goto out;
    }
}
  • 遍历叶子节点的槽位
  • index++: 每次循环索引加1
  • if (tag_get(slot, tag, j)): 检查槽位是否有指定标签
  • BUG_ON: 确保标签和数据的完整性
  • results[nr_found++] = ...: 存储到结果数组
  • 达到最大数量时退出

第七段:更新位移量和移动到下一节点

c 复制代码
                shift -= RADIX_TREE_MAP_SHIFT;
                slot = slot->slots[i];
        }
  • shift -= RADIX_TREE_MAP_SHIFT: 减少位移量
  • slot = slot->slots[i]: 移动到下一层的节点

第八段:设置下一次查找索引并返回

c 复制代码
out:
        *next_index = index;
        return nr_found;
}
  • out:: 标签,用于goto跳转
  • *next_index = index: 设置下一次查找的起始索引
  • return nr_found: 返回找到的项目数量

关键设计要点

1. 标签优化的查找

与普通查找相比,这个函数的关键优势:

普通查找 __lookup:

  • 检查槽位是否非空:if (slot->slots[i] != NULL)

标签查找 __lookup_tag:

  • 检查槽位是否有标签:if (tag_get(slot, tag, i))

2. 数据完整性检查

c 复制代码
BUG_ON(slot->slots[i] == NULL);

这个检查确保:

  • 如果节点标记了标签,对应的数据项必须存在
  • 防止标签系统和数据系统的不一致
  • 在开发阶段捕获潜在的bug

跨节点的连续批量查找带特定标签的数据项radix_tree_gang_lookup_tag

c 复制代码
unsigned int
radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results,
                unsigned long first_index, unsigned int max_items, int tag)
{
        const unsigned long max_index = radix_tree_maxindex(root->height);
        unsigned long cur_index = first_index;
        unsigned int ret = 0;

        while (ret < max_items) {
                unsigned int nr_found;
                unsigned long next_index;       /* Index of next search */

                if (cur_index > max_index)
                        break;
                nr_found = __lookup_tag(root, results + ret, cur_index,
                                        max_items - ret, &next_index, tag);
                ret += nr_found;
                if (next_index == 0)
                        break;
                cur_index = next_index;
        }
        return ret;
}

函数整体功能

这个函数是基数树标签系统的主要批量查找接口,通过多次调用 __lookup_tag 来实现跨节点的连续批量查找带特定标签的数据项

代码分段详解

第一段:函数定义和参数

c 复制代码
unsigned int
radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results,
                unsigned long first_index, unsigned int max_items, int tag)
  • unsigned int: 返回找到的总项目数量
  • root: 基数树的根节点指针
  • results: 结果数组,存储找到的所有数据项指针
  • first_index: 起始查找索引
  • max_items: 最大返回项目数量
  • tag: 要查找的标签编号

第二段:变量声明和初始化

c 复制代码
        const unsigned long max_index = radix_tree_maxindex(root->height);
        unsigned long cur_index = first_index;
        unsigned int ret = 0;
  • max_index = radix_tree_maxindex(root->height): 计算树能容纳的最大索引值
  • cur_index = first_index: 当前查找索引,初始化为起始索引
  • ret = 0: 找到的项目总数计数器,初始为0

第三段:主循环 - 批量查找核心

c 复制代码
        while (ret < max_items) {
  • while (ret < max_items): 主循环条件,当已找到的项目数小于目标数量时继续查找
  • 这个循环确保会跨多个节点进行连续查找,直到找到足够数量的带标签数据或遍历完所有数据

第四段:检查索引范围

c 复制代码
                if (cur_index > max_index)
                        break;
  • if (cur_index > max_index): 检查当前索引是否超出树的最大容量
  • break: 如果超出范围,跳出循环停止查找
  • 作用: 防止无效的查找操作

第五段:变量声明

c 复制代码
                unsigned int nr_found;
                unsigned long next_index;       /* Index of next search */
  • nr_found: 本次调用找到的项目数量
  • next_index: 下一次查找的起始索引

第六段:调用底层标签查找函数

c 复制代码
                nr_found = __lookup_tag(root, results + ret, cur_index,
                                        max_items - ret, &next_index, tag);

参数分解:

  • root: 基数树根节点
  • results + ret: 结果数组的当前位置指针
    • ret 是已找到的项目数
    • results + ret 指向结果数组中下一个空闲位置
  • cur_index: 本次查找的起始索引
  • max_items - ret: 本次查找的最大项目数(剩余需要的数量)
  • &next_index: 输出参数,接收下一次查找的起始索引
  • tag: 要查找的标签编号

作用:cur_index 开始查找最多 max_items - ret 个带标签的项目,结果存入 results 数组的剩余空间。

第七段:更新找到的项目计数

c 复制代码
                ret += nr_found;
  • ret += nr_found: 将本次找到的项目数累加到总计数中

第八段:检查查找终止条件

c 复制代码
                if (next_index == 0)
                        break;
  • if (next_index == 0): 检查下一次查找索引是否为0
  • break: 如果为0,跳出循环停止查找
  • 为什么 next_index 为0表示结束?
    • 在32位系统中,索引从最大值回绕到0表示已遍历完所有索引
    • __lookup_tag 中遇到这种情况会设置 next_index = 0

第九段:更新下一次查找索引

c 复制代码
                cur_index = next_index;
        }
  • cur_index = next_index: 将当前索引更新为下一次查找的起始位置
  • 循环继续,从新的位置开始下一轮查找

第十段:返回总项目数

c 复制代码
        return ret;
}
  • return ret: 返回最终找到的项目总数
  • 可能小于 max_items(如果树中没有足够的带标签数据项)
相关推荐
我也想失去烦恼5 小时前
Linux系统/etc/hosts文件中配置了主机解析,但还是无法解析ip
linux·运维·服务器
deng-c-f7 小时前
Linux C/C++ 学习日记(29):IO密集型与CPU密集型、CPU的调度与线程切换
linux·学习·线程·cpu·io密集·cpu密集
报错小能手10 小时前
linux学习笔记(43)网络编程——HTTPS (补充)
linux·网络·学习
报错小能手10 小时前
linux学习笔记(45)git详解
linux·笔记·学习
Maple_land11 小时前
常见Linux环境变量深度解析
linux·运维·服务器·c++·centos
小白银子12 小时前
零基础从头教学Linux(Day 53)
linux·运维·python
skywalk816313 小时前
基于频域的数字盲水印blind-watermark
linux·开发语言·python
Wang's Blog14 小时前
Linux小课堂: 定时与延时执行机制之date、at、sleep 与 crontab 的深度解析
linux·运维·服务器
被漂一组15 小时前
在线解决window和linux对linux远程问题
linux·运维·服务器