获取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
管理内存资源
性能优化策略
预分配机制
c
idr_pre_get() // 批量预分配IDR层
- 减少系统调用: 避免每次分配都进行内存分配
- 缓存友好: 重用最近释放的内存层
- 锁优化: 预分配阶段无锁,分配阶段短临界区
惰性树构建
c
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分配算法
c
// 从起始ID开始深度优先搜索
while (1) {
n = (id >> (IDR_BITS*l)) & IDR_MASK; // 计算层索引
m = find_next_bit(&bm, IDR_SIZE, n); // 查找空闲槽位
// 调整ID匹配找到的槽位
}
回溯机制
当当前分支无空间时,智能回退到父层:
c
l++; // 回退到上层
id = (id | ((1 << (IDR_BITS*l))-1)) + 1; // 调整到下一块起始
设计优势总结
可扩展性
- 支持从几个到数百万个ID的分配
- 动态树结构自动适应不同规模的ID需求
- 内存使用与实际需求成比例
高性能
- 位操作实现高效槽位查找
- 预分配机制减少系统调用开销
- 缓存友好的内存访问模式
灵活性
- 支持稀疏ID分布
- 动态树结构调整
- 可配置的层数和槽位数
为 proc 文件系统分配唯一的 inode
号get_inode_number
c
/*
* 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;
}
函数签名和注释
c
/*
* 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
: 只在当前文件中可见
第一段:变量声明
c
int i, inum = 0;
int error;
i
: 用于存储 IDR 分配的内部 IDinum
: 最终返回的inode
号,初始化为 0(错误值)error
: 错误码
第二段:IDR 预分配准备
c
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
c
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)
- 释放自旋锁
第四段:错误处理和重试
c
if (error == -EAGAIN)
goto retry;
else if (error)
return 0;
- 重试条件 :
error == -EAGAIN
- 如果错误码表示需要重试 - 跳转重试 :
goto retry
- 回到预分配步骤重新尝试 - 其他错误: 如果是其他错误,返回 0 表示失败
-EAGAIN 场景:IDR 需要扩展但预分配时没有准备足够内存,需要重新预分配
第五段:计算最终 inode
号
c
inum = (i & MAX_ID_MASK) + PROC_DYNAMIC_FIRST;
- 掩码处理 :
i & MAX_ID_MASK
- 确保ID在有效范围内 - 偏移调整 :
+ PROC_DYNAMIC_FIRST
- 加上动态inode
号的起始偏移 - 结果 : 得到最终的
proc inode
号
第六段:注释和返回
c
/* inum will never be more than 0xf0ffffff, so no check
* for overflow.
*/
return inum;
}
- 返回结果 : 返回计算得到的
inode
号
为IDR分配器预分配足够的内存层idr_pre_get
c
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;
}
函数签名
c
int idr_pre_get(struct idr *idp, unsigned gfp_mask)
{
- 返回值 :
int
- 成功返回1,失败返回0 - 参数 :
struct idr *idp
: 指向IDR结构的指针unsigned gfp_mask
: 内存分配标志
第一段:循环条件
c
while (idp->id_free_cnt < IDR_FREE_MAX) {
- 循环条件 :
idp->id_free_cnt < IDR_FREE_MAX
idp->id_free_cnt
: IDR结构中空闲层数量的计数器IDR_FREE_MAX
: 预定义的最大空闲层数量常量- 目的: 当空闲层数量不足时,持续分配直到达到最大值
第二段:内存分配
c
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层结构体
第三段:分配失败检查
c
if(new == NULL)
return (0);
- 空指针检查 :
if(new == NULL)
- 检查内存分配是否成功 - 失败返回 :
return (0)
- 如果分配失败,立即返回0表示失败 - 资源不足: 通常是因为系统内存不足
第四段:添加到空闲链表
c
free_layer(idp, new);
}
- 释放层 :
free_layer(idp, new)
- 将新分配的层添加到IDR的空闲链表中 - 函数作用 : 增加
idp->id_free_cnt
计数器,将层放入空闲链表 - 循环继续: 回到while条件检查,直到空闲层数量足够
第五段:成功返回
c
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
c
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层
函数签名
c
static struct idr_layer *alloc_layer(struct idr *idp)
{
- 返回值 :
struct idr_layer *
- 成功返回分配的层指针,失败返回NULL - 参数 :
struct idr *idp
- 指向IDR结构的指针 static
: 只在当前文件中可见
第一段:变量声明和加锁
c
struct idr_layer *p;
spin_lock(&idp->lock);
- 变量声明 :
p
- 用于存储分配的IDR层指针 - 获取锁 :
spin_lock(&idp->lock)
- 获取IDR结构的自旋锁 - 目的: 保护对IDR空闲链表的并发访问
第二段:从空闲链表获取层
c
if ((p = idp->id_free)) {
- 条件赋值 :
(p = idp->id_free)
- 将空闲链表头赋值给p,并检查是否为NULL - 链表检查: 如果空闲链表不为空,进入if块
idp->id_free
: 指向空闲链表第一个IDR层的指针
第三段:更新空闲链表
c
idp->id_free = p->ary[0];
idp->id_free_cnt--;
- 移动链表头 :
idp->id_free = p->ary[0]
- 将链表头指向下一个空闲层 - 减少计数器 :
idp->id_free_cnt--
- 空闲层数量减1 - 链表操作: 相当于从链表头部移除一个节点
第四段:初始化分配的层
c
p->ary[0] = NULL;
}
- 清空指针 :
p->ary[0] = NULL
- 清空分配层的第一个指针槽 - 准备使用: 确保层处于干净状态,可供后续使用
第五段:释放锁并返回
c
spin_unlock(&idp->lock);
return(p);
}
- 释放锁 :
spin_unlock(&idp->lock)
- 释放IDR自旋锁 - 返回结果 :
return(p)
- 返回分配的层指针(如果链表为空则返回NULL)
函数2:free_layer - 释放IDR层
函数签名
c
static void free_layer(struct idr *idp, struct idr_layer *p)
{
- 返回值 :
void
- 没有返回值 - 参数 :
struct idr *idp
: 指向IDR结构的指针struct idr_layer *p
: 要释放的IDR层指针
第一段:注释说明
c
/*
* Depends on the return element being zeroed.
*/
- 重要注释: 说明这个函数依赖于返回的元素被清零
- 隐含要求 : 调用者应该确保
p->ary[0]
已经被清空
第二段:加锁操作
c
spin_lock(&idp->lock);
- 获取锁 :
spin_lock(&idp->lock)
- 获取IDR结构的自旋锁 - 目的: 保护对IDR空闲链表的并发访问
第三段:添加到空闲链表头部
c
p->ary[0] = idp->id_free;
idp->id_free = p;
- 设置next指针 :
p->ary[0] = idp->id_free
- 将当前层指向原链表头 - 更新链表头 :
idp->id_free = p
- 将链表头指向新释放的层 - 链表操作: 相当于在链表头部插入一个新节点
第四段:更新计数器并释放锁
c
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
c
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;
}
函数签名
c
int idr_get_new(struct idr *idp, void *ptr, int *id)
{
- 返回值 :
int
- 成功返回0,失败返回错误码 - 参数 :
struct idr *idp
: 指向IDR结构的指针void *ptr
: 要与ID关联的数据指针int *id
: 输出参数,存储分配到的ID
第一段:变量声明和核心调用
c
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
c
if (rv < 0) {
if (rv == -1)
return -EAGAIN;
- 错误检查 :
if (rv < 0)
- 检查返回值是否为错误 - 特定错误 :
if (rv == -1)
- 检查是否为-1错误 - 返回标准错误 :
return -EAGAIN
- 转换为"再试一次"错误码
-EAGAIN 含义: 临时性失败,建议调用者重试操作
第三段:错误处理 - ENOSPC
c
else /* Will be -3 */
return -ENOSPC;
- 其他错误 :
else
- 处理所有其他负值错误 - 返回标准错误 :
return -ENOSPC
- 转换为"没有空间"错误码
-ENOSPC 含义: 没有可用的ID空间,永久性失败
第五段:成功处理
c
}
*id = rv;
return 0;
}
- 成功路径 : 如果
rv >= 0
,表示分配成功 - 设置输出ID :
*id = rv
- 将分配到的ID写入输出参数 - 返回成功 :
return 0
- 返回0表示操作成功
实际执行ID分配函数idr_get_new_above_int
c
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);
}
函数签名
c
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
第一段:变量声明
c
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开始
第二段:标签和初始层获取
c
build_up:
p = idp->top;
layers = idp->layers;
- 标签 :
build_up:
- 用于重试的跳转标签 - 获取顶层 :
p = idp->top
- 获取IDR的顶层指针 - 获取层数 :
layers = idp->layers
- 获取当前层数
第三段:空IDR初始化
c
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层数循环
c
while ((layers < MAX_LEVEL) && (id >= (1 << (layers*IDR_BITS)))) {
- 循环条件 :
layers < MAX_LEVEL
: 层数未达到最大值id >= (1 << (layers*IDR_BITS))
: 请求的ID超出当前层数的容量
- 容量计算 :
1 << (layers*IDR_BITS)
- 当前层数能表示的最大ID+1
第五段:增加层数
c
layers++;
if (!p->count)
continue;
- 增加层数 :
layers++
- 需要增加一层 - 空层检查 :
if (!p->count)
- 如果当前层是空的,继续下一轮循环 - 优化: 空层不需要立即分配新层,可以延迟
第六段:分配新层
c
if (!(new = alloc_layer(idp))) {
- 分配新层 :
new = alloc_layer(idp)
- 尝试分配新的IDR层 - 分配失败检查: 如果分配失败,进入错误处理
第七段:分配失败的回滚处理
c
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错误
第八段:构建新层结构
c
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结构
c
idp->top = p;
idp->layers = layers;
- 更新顶层 :
idp->top = p
- 设置新的顶层指针 - 更新层数 :
idp->layers = layers
- 设置新的层数
第十段:执行子分配
c
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. 动态扩展策略
c
while ((layers < MAX_LEVEL) && (id >= (1 << (layers*IDR_BITS))))
- 按需扩展: 只有当请求的ID超出当前容量时才扩展
- 渐进增长: 每次增加一层,容量指数级增长
- 容量检查: 数学计算确保扩展的正确性
2. 空层优化
c
if (!p->count)
continue;
- 延迟分配: 如果当前层是空的,跳过立即分配
- 性能优化: 避免不必要的内存分配
- 资源节约: 只在真正需要时分配新层
在已构建的IDR树结构中实际分配一个可用的IDsub_alloc
c
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);
}
函数签名
c
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
第一段:变量声明
c
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开始
第二段:初始化遍历状态
c
p = idp->top;
l = idp->layers;
pa[l--] = NULL;
- 获取顶层 :
p = idp->top
- 从IDR顶层开始 - 获取层数 :
l = idp->layers
- 当前总层数 - 路径数组 :
pa[l--] = NULL
- 初始化路径数组,层数递减
第三段:主循环开始
c
while (1) {
/*
* We run around this while until we reach the leaf node...
*/
- 无限循环: 直到找到叶子节点或失败
第四段:计算当前层索引
c
n = (id >> (IDR_BITS*l)) & IDR_MASK;
- 提取层索引: 从ID中提取当前层的索引
- 位操作 :
id >> (IDR_BITS*l)
: 将ID右移到当前层& IDR_MASK
: 取模得到当前层槽位索引
第五段:查找可用槽位
c
bm = ~p->bitmap;
m = find_next_bit(&bm, IDR_SIZE, n);
- 反转位图 :
bm = ~p->bitmap
- 将已使用的位反转,得到空闲位 - 查找空闲位 :
find_next_bit(&bm, IDR_SIZE, n)
- 从位置n开始查找第一个空闲位
第六段:当前层无空闲槽位处理
c
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以匹配找到的槽位
c
if (m != n) {
sh = IDR_BITS*l;
id = ((id >> sh) ^ n ^ m) << sh;
}
- 槽位不匹配: 如果找到的槽位m不等于期望的n
- 计算位移 :
sh = IDR_BITS*l
- 当前层的位移量 - 调整ID: 将ID的当前层部分从n改为m,保持其他层不变
第八段:ID范围检查
c
if ((id >= MAX_ID_BIT) || (id < 0))
return -3;
- 范围检查: 确保ID在有效范围内
- 超出范围: 如果ID超出最大限制或为负,返回-3(空间不足)
第九段:到达叶子节点检查
c
if (l == 0)
break;
- 叶子节点检查 :
l == 0
- 如果到达最底层(叶子层) - 跳出循环: 找到目标位置,跳出循环
第十段:创建缺失的中间层
c
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++
- 增加子层计数
第十一段:继续向下遍历
c
pa[l--] = p;
p = p->ary[m];
}
- 保存路径 :
pa[l--] = p
- 将当前层保存到路径数组 - 移动到下层 :
p = p->ary[m]
- 进入下一层 - 层数递减 :
l--
- 层数减1
第十二段:设置叶子节点数据
c
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++
- 增加叶子节点使用计数
第十三段:向上传播满状态
c
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)
- 标记父层对应槽位
第十四段:返回结果
c
return(id);
}
- 返回分配的ID: 成功完成分配,返回最终分配的ID
函数功能总结
主要功能: 在已构建的IDR树结构中实际分配一个可用的ID,并关联用户数据
关键设计细节
1. 路径记录数组
c
struct idr_layer *pa[MAX_LEVEL];
- 记录遍历路径: 保存从根到叶子的所有层指针
- 支持回溯: 当需要回退到上层时使用
- 满状态传播: 用于向上标记满状态
2. 位图管理策略
c
bm = ~p->bitmap;
m = find_next_bit(&bm, IDR_SIZE, n);
- 高效查找: 使用位操作快速查找空闲槽位
- 从起始位置开始: 从期望位置n开始查找
- 位图反转: 将已使用位图反转得到空闲位图
3. 动态层创建
c
if (!p->ary[m]) {
new = alloc_layer(idp);
p->ary[m] = new;
}
- 惰性分配: 只在需要时创建中间层
- 节省内存: 稀疏ID分布时节省大量内存
- 按需扩展: 随着ID分配自动扩展树结构
4. ID调整算法
c
id = ((id >> sh) ^ n ^ m) << sh;
- 精确调整: 只改变当前层的索引,保持其他层不变
- 位操作高效: 使用异或和移位快速调整
- 保持连续性: 尽量保持ID的连续性
5. 满状态传播
c
while (p->bitmap == IDR_FULL) {
p = pa[++l];
__set_bit((n & IDR_MASK), &p->bitmap);
}
- 优化查找: 标记满的子树,避免无效搜索
- 自底向上: 从叶子节点向上传播
- 性能提升: 减少未来分配时的搜索范围