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 管理内存资源

性能优化策略

预分配机制

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 文件系统分配唯一的 inodeget_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_FIRST0xffffffff
  • 返回值 : 成功返回 inode 号,失败返回 0
  • static: 只在当前文件中可见

第一段:变量声明

c 复制代码
        int i, inum = 0;
        int error;
  • i: 用于存储 IDR 分配的内部 ID
  • inum : 最终返回的 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_layerfree_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: 当前处理的ID
    • bm: 位图操作变量
  • 初始化 : 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);
}
  • 优化查找: 标记满的子树,避免无效搜索
  • 自底向上: 从叶子节点向上传播
  • 性能提升: 减少未来分配时的搜索范围
相关推荐
一文解千机5 小时前
Termux 安装盘搜搜PanSou,快速找到网盘资源链接,支持各大网盘,自定义部署,数据存储到手机,打造移动搜索资源库
linux·termux·盘搜搜·pansou·资源搜索·网盘资源链接搜索·手机网盘搜索
啊森要自信6 小时前
【MySQL 数据库】使用C语言操作MySQL
linux·c语言·开发语言·数据库·mysql
东城绝神6 小时前
《Linux运维总结:基于ARM64+X86_64架构CPU使用docker-compose一键离线部署mongodb 7.0.22容器版分片集群》
linux·运维·mongodb·架构·分片集群
peiwang2456 小时前
Linux系统中CoreDump的生成与调试
java·linux·开发语言
小立爱学习6 小时前
Linux 内存 --- get_user_pages/pin_user_pages函数
linux·c语言
江公望7 小时前
Qt enum ApplicationAttribute枚举值浅解
linux·qt
Yuki’7 小时前
Linux系统的ARM库移植
linux·arm开发
报错小能手7 小时前
linux学习笔记(51)Redis发布订阅 主从复制 缓存 雪崩
linux·笔记·学习
程序猿编码7 小时前
轻量级却实用:sigtrace 如何靠 ptrace 实现 Linux 信号的捕获与阻断(C/C++代码实现)
linux·c语言·c++·信号·捕获·ptrace