Linux中inode节点号的获取相关函数的实现
获取inode
编号系统架构总览
核心函数关系
get_inode_number() ← 用户接口↓
idr_get_new() ← 标准接口封装 ↓
idr_get_new_above_int() ← 树结构管理↓
sub_alloc() ← 实际分配算法↓
alloc_layer()/free_layer() ← 内存管理↓
idr_pre_get() ← 预分配优化
设计哲学与核心思想
分层抽象设计
- 用户层:
get_inode_number
提供简单接口 - 适配层:
idr_get_new
处理错误码标准化 - 控制层:
idr_get_new_above_int
管理树结构扩展 - 算法层:
sub_alloc
实现核心分配逻辑 - 资源层:
alloc_layer/free_layer
管理内存资源
性能优化策略
预分配机制
idr_pre_get() // 批量预分配IDR层
- 减少系统调用: 避免每次分配都进行内存分配
- 缓存友好: 重用最近释放的内存层
- 锁优化: 预分配阶段无锁,分配阶段短临界区
惰性树构建
if (!p->ary[m]) {new = alloc_layer(idp); // 按需创建中间层
}
- 稀疏优化: 只为实际使用的路径分配内存
- 内存效率: 避免预分配整个树结构
- 动态扩展: 自动适应ID分布模式
关键技术实现
IDR树结构设计
多层radix树
层2 (顶层) → 层1 → 层0 (叶子层)每层32槽位 (IDR_BITS=5)容量: 32^3 = 32,768个ID
- ID号被拆分成多个位段,每个位段从高到底分别处于对应的层的槽中,槽的索引就是该位段的数值
- 和基数树类似的一个数据结构,基数树节点的槽索引也是当前位段的值
- 不过基数树没有位图保存槽的使用状态,只保存了当前槽分配的个数,用来判断当前节点是否可释放
智能ID分配算法
// 从起始ID开始深度优先搜索
while (1) {n = (id >> (IDR_BITS*l)) & IDR_MASK; // 计算层索引m = find_next_bit(&bm, IDR_SIZE, n); // 查找空闲槽位// 调整ID匹配找到的槽位
}
回溯机制
当当前分支无空间时,智能回退到父层:
l++; // 回退到上层
id = (id | ((1 << (IDR_BITS*l))-1)) + 1; // 调整到下一块起始
设计优势总结
可扩展性
- 支持从几个到数百万个ID的分配
- 动态树结构自动适应不同规模的ID需求
- 内存使用与实际需求成比例
高性能
- 位操作实现高效槽位查找
- 预分配机制减少系统调用开销
- 缓存友好的内存访问模式
灵活性
- 支持稀疏ID分布
- 动态树结构调整
- 可配置的层数和槽位数
为 proc 文件系统分配唯一的 inode
号get_inode_number
/** Return an inode number between PROC_DYNAMIC_FIRST and* 0xffffffff, or zero on failure.*/
static unsigned int get_inode_number(void)
{int i, inum = 0;int error;retry:if (idr_pre_get(&proc_inum_idr, GFP_KERNEL) == 0)return 0;spin_lock(&proc_inum_lock);error = idr_get_new(&proc_inum_idr, NULL, &i);spin_unlock(&proc_inum_lock);if (error == -EAGAIN)goto retry;else if (error)return 0;inum = (i & MAX_ID_MASK) + PROC_DYNAMIC_FIRST;/* inum will never be more than 0xf0ffffff, so no check* for overflow.*/return inum;
}
函数签名和注释
/** Return an inode number between PROC_DYNAMIC_FIRST and* 0xffffffff, or zero on failure.*/
static unsigned int get_inode_number(void)
{
- 功能: 分配一个唯一的
inode
号,用于 proc 文件系统 - 范围: 从
PROC_DYNAMIC_FIRST
到0xffffffff
- 返回值: 成功返回
inode
号,失败返回 0 static
: 只在当前文件中可见
第一段:变量声明
int i, inum = 0;int error;
i
: 用于存储 IDR 分配的内部 IDinum
: 最终返回的inode
号,初始化为 0(错误值)error
: 错误码
第二段:IDR 预分配准备
retry:if (idr_pre_get(&proc_inum_idr, GFP_KERNEL) == 0)return 0;
- 标签:
retry:
- 重试跳转标签 - 预分配检查:
idr_pre_get(&proc_inum_idr, GFP_KERNEL)
proc_inum_idr
: 全局的 IDR(ID分配器)对象GFP_KERNEL
: 内存分配标志(可能睡眠)
- 失败返回: 如果预分配失败(返回 0),直接返回 0
IDR 机制:Linux 内核的整数ID分配器,用于高效分配和管理唯一ID
第三段:获取自旋锁并分配ID
spin_lock(&proc_inum_lock);error = idr_get_new(&proc_inum_idr, NULL, &i);spin_unlock(&proc_inum_lock);
- 加锁:
spin_lock(&proc_inum_lock)
- 获取保护IDR的自旋锁 - 分配ID:
idr_get_new(&proc_inum_idr, NULL, &i)
- 从IDR分配一个新的ID
NULL
: 不关联任何数据指针&i
: 输出参数,存储分配的ID
- 解锁:
spin_unlock(&proc_inum_lock)
- 释放自旋锁
第四段:错误处理和重试
if (error == -EAGAIN)goto retry;else if (error)return 0;
- 重试条件:
error == -EAGAIN
- 如果错误码表示需要重试 - 跳转重试:
goto retry
- 回到预分配步骤重新尝试 - 其他错误: 如果是其他错误,返回 0 表示失败
-EAGAIN 场景:IDR 需要扩展但预分配时没有准备足够内存,需要重新预分配
第五段:计算最终 inode
号
inum = (i & MAX_ID_MASK) + PROC_DYNAMIC_FIRST;
- 掩码处理:
i & MAX_ID_MASK
- 确保ID在有效范围内 - 偏移调整:
+ PROC_DYNAMIC_FIRST
- 加上动态inode
号的起始偏移 - 结果: 得到最终的
proc inode
号
第六段:注释和返回
/* inum will never be more than 0xf0ffffff, so no check* for overflow.*/return inum;
}
- 返回结果: 返回计算得到的
inode
号
为IDR分配器预分配足够的内存层idr_pre_get
int idr_pre_get(struct idr *idp, unsigned gfp_mask)
{while (idp->id_free_cnt < IDR_FREE_MAX) {struct idr_layer *new;new = kmem_cache_alloc(idr_layer_cache, gfp_mask);if(new == NULL)return (0);free_layer(idp, new);}return 1;
}
函数签名
int idr_pre_get(struct idr *idp, unsigned gfp_mask)
{
- 返回值:
int
- 成功返回1,失败返回0 - 参数:
struct idr *idp
: 指向IDR结构的指针unsigned gfp_mask
: 内存分配标志
第一段:循环条件
while (idp->id_free_cnt < IDR_FREE_MAX) {
- 循环条件:
idp->id_free_cnt < IDR_FREE_MAX
idp->id_free_cnt
: IDR结构中空闲层数量的计数器IDR_FREE_MAX
: 预定义的最大空闲层数量常量- 目的: 当空闲层数量不足时,持续分配直到达到最大值
第二段:内存分配
struct idr_layer *new;new = kmem_cache_alloc(idr_layer_cache, gfp_mask);
- 变量声明:
new
- 新分配的IDR层指针 - 内存分配:
kmem_cache_alloc(idr_layer_cache, gfp_mask)
idr_layer_cache
: 预先创建的IDR层slab缓存gfp_mask
: 内存分配标志(如GFP_KERNEL
)
- 目的: 从slab缓存中分配一个IDR层结构体
第三段:分配失败检查
if(new == NULL)return (0);
- 空指针检查:
if(new == NULL)
- 检查内存分配是否成功 - 失败返回:
return (0)
- 如果分配失败,立即返回0表示失败 - 资源不足: 通常是因为系统内存不足
第四段:添加到空闲链表
free_layer(idp, new);}
- 释放层:
free_layer(idp, new)
- 将新分配的层添加到IDR的空闲链表中 - 函数作用: 增加
idp->id_free_cnt
计数器,将层放入空闲链表 - 循环继续: 回到while条件检查,直到空闲层数量足够
第五段:成功返回
return 1;
}
- 成功返回:
return 1
- 表示预分配成功完成 - 条件满足: 现在IDR有足够的空闲层来处理后续的ID分配
函数功能总结
主要功能: 为IDR分配器预分配足够的内存层,确保后续的ID分配操作能够成功执行
IDR 层结构分析
IDR 层级结构
IDR 根 (idr)|v
层0 (idr_layer) → 层1 (idr_layer) → ... → 层N (idr_layer)| |v v
位图数组 位图数组
每个 idr_layer
包含:
- 指向子层的指针数组
- 位图信息
- 层数信息
空闲链表管理
idr → id_free: [层A] → [层B] → [层C] → NULL↑ ↑ ↑空闲链表头 预分配的 预分配的空闲层 空闲层
分配和释放IDR层alloc_layer
和free_layer
static struct idr_layer *alloc_layer(struct idr *idp)
{struct idr_layer *p;spin_lock(&idp->lock);if ((p = idp->id_free)) {idp->id_free = p->ary[0];idp->id_free_cnt--;p->ary[0] = NULL;}spin_unlock(&idp->lock);return(p);
}
static void free_layer(struct idr *idp, struct idr_layer *p)
{/** Depends on the return element being zeroed.*/spin_lock(&idp->lock);p->ary[0] = idp->id_free;idp->id_free = p;idp->id_free_cnt++;spin_unlock(&idp->lock);
}
函数1:alloc_layer - 分配IDR层
函数签名
static struct idr_layer *alloc_layer(struct idr *idp)
{
- 返回值:
struct idr_layer *
- 成功返回分配的层指针,失败返回NULL - 参数:
struct idr *idp
- 指向IDR结构的指针 static
: 只在当前文件中可见
第一段:变量声明和加锁
struct idr_layer *p;spin_lock(&idp->lock);
- 变量声明:
p
- 用于存储分配的IDR层指针 - 获取锁:
spin_lock(&idp->lock)
- 获取IDR结构的自旋锁 - 目的: 保护对IDR空闲链表的并发访问
第二段:从空闲链表获取层
if ((p = idp->id_free)) {
- 条件赋值:
(p = idp->id_free)
- 将空闲链表头赋值给p,并检查是否为NULL - 链表检查: 如果空闲链表不为空,进入if块
idp->id_free
: 指向空闲链表第一个IDR层的指针
第三段:更新空闲链表
idp->id_free = p->ary[0];idp->id_free_cnt--;
- 移动链表头:
idp->id_free = p->ary[0]
- 将链表头指向下一个空闲层 - 减少计数器:
idp->id_free_cnt--
- 空闲层数量减1 - 链表操作: 相当于从链表头部移除一个节点
第四段:初始化分配的层
p->ary[0] = NULL;}
- 清空指针:
p->ary[0] = NULL
- 清空分配层的第一个指针槽 - 准备使用: 确保层处于干净状态,可供后续使用
第五段:释放锁并返回
spin_unlock(&idp->lock);return(p);
}
- 释放锁:
spin_unlock(&idp->lock)
- 释放IDR自旋锁 - 返回结果:
return(p)
- 返回分配的层指针(如果链表为空则返回NULL)
函数2:free_layer - 释放IDR层
函数签名
static void free_layer(struct idr *idp, struct idr_layer *p)
{
- 返回值:
void
- 没有返回值 - 参数:
struct idr *idp
: 指向IDR结构的指针struct idr_layer *p
: 要释放的IDR层指针
第一段:注释说明
/** Depends on the return element being zeroed.*/
- 重要注释: 说明这个函数依赖于返回的元素被清零
- 隐含要求: 调用者应该确保
p->ary[0]
已经被清空
第二段:加锁操作
spin_lock(&idp->lock);
- 获取锁:
spin_lock(&idp->lock)
- 获取IDR结构的自旋锁 - 目的: 保护对IDR空闲链表的并发访问
第三段:添加到空闲链表头部
p->ary[0] = idp->id_free;idp->id_free = p;
- 设置next指针:
p->ary[0] = idp->id_free
- 将当前层指向原链表头 - 更新链表头:
idp->id_free = p
- 将链表头指向新释放的层 - 链表操作: 相当于在链表头部插入一个新节点
第四段:更新计数器并释放锁
idp->id_free_cnt++;spin_unlock(&idp->lock);
}
- 增加计数器:
idp->id_free_cnt++
- 空闲层数量加1 - 释放锁:
spin_unlock(&idp->lock)
- 释放IDR自旋锁
函数功能总结
数据结构分析
IDR 空闲链表结构
idp (IDR结构)↓
id_free → [层A] → [层B] → [层C] → NULL↓ ↓ ↓ary[0] ary[0] ary[0]=层B =层C =NULL
链表操作原理
alloc_layer
(分配):
分配前: id_free → [A] → [B] → [C] → NULL
分配后: id_free → [B] → [C] → NULL
返回: [A] (且 A->ary[0] = NULL)
free_layer
(释放):
释放前: id_free → [B] → [C] → NULL
释放 [A]: A->ary[0] = B (原链表头)
更新: id_free → [A] → [B] → [C] → NULL
调用ID分配函数idr_get_new
int idr_get_new(struct idr *idp, void *ptr, int *id)
{int rv;rv = idr_get_new_above_int(idp, ptr, 0);/** This is a cheap hack until the IDR code can be fixed to* return proper error values.*/if (rv < 0) {if (rv == -1)return -EAGAIN;else /* Will be -3 */return -ENOSPC;}*id = rv;return 0;
}
函数签名
int idr_get_new(struct idr *idp, void *ptr, int *id)
{
- 返回值:
int
- 成功返回0,失败返回错误码 - 参数:
struct idr *idp
: 指向IDR结构的指针void *ptr
: 要与ID关联的数据指针int *id
: 输出参数,存储分配到的ID
第一段:变量声明和核心调用
int rv;rv = idr_get_new_above_int(idp, ptr, 0);
- 变量声明:
rv
- 存储底层函数的返回值 - 调用核心函数:
idr_get_new_above_int(idp, ptr, 0)
idp
: 传递的IDR结构指针ptr
: 要关联的数据指针0
: 起始ID号,表示从最小的可用ID开始分配
- 功能: 实际执行ID分配的核心函数
第二段:错误处理 - EAGAIN
if (rv < 0) {if (rv == -1)return -EAGAIN;
- 错误检查:
if (rv < 0)
- 检查返回值是否为错误 - 特定错误:
if (rv == -1)
- 检查是否为-1错误 - 返回标准错误:
return -EAGAIN
- 转换为"再试一次"错误码
-EAGAIN 含义: 临时性失败,建议调用者重试操作
第三段:错误处理 - ENOSPC
else /* Will be -3 */return -ENOSPC;
- 其他错误:
else
- 处理所有其他负值错误 - 返回标准错误:
return -ENOSPC
- 转换为"没有空间"错误码
-ENOSPC 含义: 没有可用的ID空间,永久性失败
第五段:成功处理
}*id = rv;return 0;
}
- 成功路径: 如果
rv >= 0
,表示分配成功 - 设置输出ID:
*id = rv
- 将分配到的ID写入输出参数 - 返回成功:
return 0
- 返回0表示操作成功
实际执行ID分配函数idr_get_new_above_int
static int idr_get_new_above_int(struct idr *idp, void *ptr, int starting_id)
{struct idr_layer *p, *new;int layers, v, id;id = starting_id;
build_up:p = idp->top;layers = idp->layers;if (unlikely(!p)) {if (!(p = alloc_layer(idp)))return -1;layers = 1;}/** Add a new layer to the top of the tree if the requested* id is larger than the currently allocated space.*/while ((layers < MAX_LEVEL) && (id >= (1 << (layers*IDR_BITS)))) {layers++;if (!p->count)continue;if (!(new = alloc_layer(idp))) {/** The allocation failed. If we built part of* the structure tear it down.*/for (new = p; p && p != idp->top; new = p) {p = p->ary[0];new->ary[0] = NULL;new->bitmap = new->count = 0;free_layer(idp, new);}return -1;}new->ary[0] = p;new->count = 1;if (p->bitmap == IDR_FULL)__set_bit(0, &new->bitmap);p = new;}idp->top = p;idp->layers = layers;v = sub_alloc(idp, ptr, &id);if (v == -2)goto build_up;return(v);
}
函数签名
static int idr_get_new_above_int(struct idr *idp, void *ptr, int starting_id)
{
- 返回值:
int
- 成功返回分配的ID,失败返回错误码 - 参数:
struct idr *idp
: 指向IDR结构的指针void *ptr
: 要与ID关联的数据指针int starting_id
: 起始ID号,从该值开始寻找可用ID
第一段:变量声明
struct idr_layer *p, *new;int layers, v, id;id = starting_id;
- 变量声明:
p, new
: IDR层指针,用于遍历和创建新层layers
: 当前IDR的层数v
: 子分配函数的返回值id
: 要分配的ID,初始化为起始ID
- 初始化:
id = starting_id
- 从指定的起始ID开始
第二段:标签和初始层获取
build_up:p = idp->top;layers = idp->layers;
- 标签:
build_up:
- 用于重试的跳转标签 - 获取顶层:
p = idp->top
- 获取IDR的顶层指针 - 获取层数:
layers = idp->layers
- 获取当前层数
第三段:空IDR初始化
if (unlikely(!p)) {if (!(p = alloc_layer(idp)))return -1;layers = 1;}
- 空检查:
if (unlikely(!p))
- 检查IDR是否为空(没有顶层) - 分配新层:
p = alloc_layer(idp)
- 分配第一个IDR层 - 分配失败: 如果分配失败,返回-1(EAGAIN)
- 设置层数:
layers = 1
- 新IDR只有一层
第四段:扩展IDR层数循环
while ((layers < MAX_LEVEL) && (id >= (1 << (layers*IDR_BITS)))) {
- 循环条件:
layers < MAX_LEVEL
: 层数未达到最大值id >= (1 << (layers*IDR_BITS))
: 请求的ID超出当前层数的容量
- 容量计算:
1 << (layers*IDR_BITS)
- 当前层数能表示的最大ID+1
第五段:增加层数
layers++;if (!p->count)continue;
- 增加层数:
layers++
- 需要增加一层 - 空层检查:
if (!p->count)
- 如果当前层是空的,继续下一轮循环 - 优化: 空层不需要立即分配新层,可以延迟
第六段:分配新层
if (!(new = alloc_layer(idp))) {
- 分配新层:
new = alloc_layer(idp)
- 尝试分配新的IDR层 - 分配失败检查: 如果分配失败,进入错误处理
第七段:分配失败的回滚处理
for (new = p; p && p != idp->top; new = p) {p = p->ary[0];new->ary[0] = NULL;new->bitmap = new->count = 0;free_layer(idp, new);}return -1;
- 回滚循环: 遍历并释放部分构建的结构
- 指针清理:
new->ary[0] = NULL
- 清空指针 - 状态重置:
new->bitmap = new->count = 0
- 重置状态 - 释放层:
free_layer(idp, new)
- 将层返回空闲链表 - 返回错误:
return -1
- 返回EAGAIN错误
第八段:构建新层结构
new->ary[0] = p;new->count = 1;if (p->bitmap == IDR_FULL)__set_bit(0, &new->bitmap);p = new;}
- 设置子层:
new->ary[0] = p
- 新层指向原来子层 - 设置计数:
new->count = 1
- 新层有一个子层 - 位图设置: 如果原来子层已满,设置新层的位图
- 更新指针:
p = new
- 将新层设为当前层
第九段:更新IDR结构
idp->top = p;idp->layers = layers;
- 更新顶层:
idp->top = p
- 设置新的顶层指针 - 更新层数:
idp->layers = layers
- 设置新的层数
第十段:执行子分配
v = sub_alloc(idp, ptr, &id);if (v == -2)goto build_up;return(v);
}
- 子分配调用:
v = sub_alloc(idp, ptr, &id)
- 在构建好的结构中分配ID - 冲突重试:
if (v == -2)
- 如果发生冲突,重新构建结构 - 返回结果:
return(v)
- 返回分配结果(成功ID或错误码)
函数功能总结
主要功能: 在IDR树中分配一个新的ID,必要时扩展树结构以容纳更大的ID值
关键设计细节
1. 动态扩展策略
while ((layers < MAX_LEVEL) && (id >= (1 << (layers*IDR_BITS))))
- 按需扩展: 只有当请求的ID超出当前容量时才扩展
- 渐进增长: 每次增加一层,容量指数级增长
- 容量检查: 数学计算确保扩展的正确性
2. 空层优化
if (!p->count)continue;
- 延迟分配: 如果当前层是空的,跳过立即分配
- 性能优化: 避免不必要的内存分配
- 资源节约: 只在真正需要时分配新层
在已构建的IDR树结构中实际分配一个可用的IDsub_alloc
static int sub_alloc(struct idr *idp, void *ptr, int *starting_id)
{int n, m, sh;struct idr_layer *p, *new;struct idr_layer *pa[MAX_LEVEL];int l, id;long bm;id = *starting_id;p = idp->top;l = idp->layers;pa[l--] = NULL;while (1) {/** We run around this while until we reach the leaf node...*/n = (id >> (IDR_BITS*l)) & IDR_MASK;bm = ~p->bitmap;m = find_next_bit(&bm, IDR_SIZE, n);if (m == IDR_SIZE) {/* no space available go back to previous layer. */l++;id = (id | ((1 << (IDR_BITS*l))-1)) + 1;if (!(p = pa[l])) {*starting_id = id;return -2;}continue;}if (m != n) {sh = IDR_BITS*l;id = ((id >> sh) ^ n ^ m) << sh;}if ((id >= MAX_ID_BIT) || (id < 0))return -3;if (l == 0)break;/** Create the layer below if it is missing.*/if (!p->ary[m]) {if (!(new = alloc_layer(idp)))return -1;p->ary[m] = new;p->count++;}pa[l--] = p;p = p->ary[m];}/** We have reached the leaf node, plant the* users pointer and return the raw id.*/p->ary[m] = (struct idr_layer *)ptr;__set_bit(m, &p->bitmap);p->count++;/** If this layer is full mark the bit in the layer above* to show that this part of the radix tree is full.* This may complete the layer above and require walking* up the radix tree.*/n = id;while (p->bitmap == IDR_FULL) {if (!(p = pa[++l]))break;n = n >> IDR_BITS;__set_bit((n & IDR_MASK), &p->bitmap);}return(id);
}
函数签名
static int sub_alloc(struct idr *idp, void *ptr, int *starting_id)
{
- 返回值:
int
- 成功返回分配的ID,失败返回错误码 - 参数:
struct idr *idp
: 指向IDR结构的指针void *ptr
: 要与ID关联的数据指针int *starting_id
: 输入输出参数,起始ID和最终分配的ID
第一段:变量声明
int n, m, sh;struct idr_layer *p, *new;struct idr_layer *pa[MAX_LEVEL];int l, id;long bm;id = *starting_id;
- 变量声明:
n, m, sh
: 临时整数变量,用于位操作p, new
: IDR层指针pa[MAX_LEVEL]
: 路径数组,记录遍历路径l
: 当前层数id
: 当前处理的IDbm
: 位图操作变量
- 初始化:
id = *starting_id
- 从起始ID开始
第二段:初始化遍历状态
p = idp->top;l = idp->layers;pa[l--] = NULL;
- 获取顶层:
p = idp->top
- 从IDR顶层开始 - 获取层数:
l = idp->layers
- 当前总层数 - 路径数组:
pa[l--] = NULL
- 初始化路径数组,层数递减
第三段:主循环开始
while (1) {/** We run around this while until we reach the leaf node...*/
- 无限循环: 直到找到叶子节点或失败
第四段:计算当前层索引
n = (id >> (IDR_BITS*l)) & IDR_MASK;
- 提取层索引: 从ID中提取当前层的索引
- 位操作:
id >> (IDR_BITS*l)
: 将ID右移到当前层& IDR_MASK
: 取模得到当前层槽位索引
第五段:查找可用槽位
bm = ~p->bitmap;m = find_next_bit(&bm, IDR_SIZE, n);
- 反转位图:
bm = ~p->bitmap
- 将已使用的位反转,得到空闲位 - 查找空闲位:
find_next_bit(&bm, IDR_SIZE, n)
- 从位置n开始查找第一个空闲位
第六段:当前层无空闲槽位处理
if (m == IDR_SIZE) {/* no space available go back to previous layer. */l++;id = (id | ((1 << (IDR_BITS*l))-1)) + 1;if (!(p = pa[l])) {*starting_id = id;return -2;}continue;}
- 无空间检查:
m == IDR_SIZE
- 当前层没有空闲槽位 - 回退到上层:
l++
- 回到上一层 - ID调整:
id = (id | ((1 << (IDR_BITS*l))-1)) + 1
- 将ID设置为下一块的起始 - 路径检查: 如果已经回到顶层且无空间,返回-2,重新基于新
id
构建层结构 - 继续循环: 在上一层继续查找
第七段:调整ID以匹配找到的槽位
if (m != n) {sh = IDR_BITS*l;id = ((id >> sh) ^ n ^ m) << sh;}
- 槽位不匹配: 如果找到的槽位m不等于期望的n
- 计算位移:
sh = IDR_BITS*l
- 当前层的位移量 - 调整ID: 将ID的当前层部分从n改为m,保持其他层不变
第八段:ID范围检查
if ((id >= MAX_ID_BIT) || (id < 0))return -3;
- 范围检查: 确保ID在有效范围内
- 超出范围: 如果ID超出最大限制或为负,返回-3(空间不足)
第九段:到达叶子节点检查
if (l == 0)break;
- 叶子节点检查:
l == 0
- 如果到达最底层(叶子层) - 跳出循环: 找到目标位置,跳出循环
第十段:创建缺失的中间层
if (!p->ary[m]) {if (!(new = alloc_layer(idp)))return -1;p->ary[m] = new;p->count++;}
- 子层检查:
!p->ary[m]
- 检查下一层是否存在 - 分配新层:
new = alloc_layer(idp)
- 分配新的IDR层 - 分配失败: 返回-1(EAGAIN)
- 设置指针:
p->ary[m] = new
- 连接新层 - 增加计数:
p->count++
- 增加子层计数
第十一段:继续向下遍历
pa[l--] = p;p = p->ary[m];}
- 保存路径:
pa[l--] = p
- 将当前层保存到路径数组 - 移动到下层:
p = p->ary[m]
- 进入下一层 - 层数递减:
l--
- 层数减1
第十二段:设置叶子节点数据
p->ary[m] = (struct idr_layer *)ptr;__set_bit(m, &p->bitmap);p->count++;
- 存储数据:
p->ary[m] = (struct idr_layer *)ptr
- 在叶子节点存储用户数据 - 标记使用:
__set_bit(m, &p->bitmap)
- 标记该槽位已使用 - 增加计数:
p->count++
- 增加叶子节点使用计数
第十三段:向上传播满状态
n = id;while (p->bitmap == IDR_FULL) {if (!(p = pa[++l]))break;n = n >> IDR_BITS;__set_bit((n & IDR_MASK), &p->bitmap);}
- 保存ID:
n = id
- 保存分配的ID用于向上传播 - 满状态传播: 如果当前层已满,向上层传播
- 移动到上层:
p = pa[++l]
- 回到父层 - 计算父层索引:
n = n >> IDR_BITS
- 获取在父层的索引 - 标记父层:
__set_bit((n & IDR_MASK), &p->bitmap)
- 标记父层对应槽位
第十四段:返回结果
return(id);
}
- 返回分配的ID: 成功完成分配,返回最终分配的ID
函数功能总结
主要功能: 在已构建的IDR树结构中实际分配一个可用的ID,并关联用户数据
关键设计细节
1. 路径记录数组
struct idr_layer *pa[MAX_LEVEL];
- 记录遍历路径: 保存从根到叶子的所有层指针
- 支持回溯: 当需要回退到上层时使用
- 满状态传播: 用于向上标记满状态
2. 位图管理策略
bm = ~p->bitmap;
m = find_next_bit(&bm, IDR_SIZE, n);
- 高效查找: 使用位操作快速查找空闲槽位
- 从起始位置开始: 从期望位置n开始查找
- 位图反转: 将已使用位图反转得到空闲位图
3. 动态层创建
if (!p->ary[m]) {new = alloc_layer(idp);p->ary[m] = new;
}
- 惰性分配: 只在需要时创建中间层
- 节省内存: 稀疏ID分布时节省大量内存
- 按需扩展: 随着ID分配自动扩展树结构
4. ID调整算法
id = ((id >> sh) ^ n ^ m) << sh;
- 精确调整: 只改变当前层的索引,保持其他层不变
- 位操作高效: 使用异或和移位快速调整
- 保持连续性: 尽量保持ID的连续性
5. 满状态传播
while (p->bitmap == IDR_FULL) {p = pa[++l];__set_bit((n & IDR_MASK), &p->bitmap);
}
- 优化查找: 标记满的子树,避免无效搜索
- 自底向上: 从叶子节点向上传播
- 性能提升: 减少未来分配时的搜索范围