查找从指定索引开始的多个数据项__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++
: 每次循环索引加1if (slot->slots[j])
: 如果槽位有数据results[nr_found++] = ...
: 存储到结果数组,计数器加1if (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)
: 检查下一次查找索引是否为0break
: 如果为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++
: 每次循环索引加1if (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)
: 检查下一次查找索引是否为0break
: 如果为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
(如果树中没有足够的带标签数据项)