Linux中匿名设备和安全相关以及VFS的slab缓存对象创建
初始化未命名设备号的 ID 分配unnamed_dev_init
void __init unnamed_dev_init(void)
{idr_init(&unnamed_dev_idr);
}
static struct idr unnamed_dev_idr;
void idr_init(struct idr *idp)
{init_id_cache();memset(idp, 0, sizeof(struct idr));spin_lock_init(&idp->lock);
}static int init_id_cache(void)
{if (!idr_layer_cache)idr_layer_cache = kmem_cache_create("idr_layer_cache",sizeof(struct idr_layer), 0, 0, idr_cache_ctor, NULL);return 0;
}
整体功能概述
这些函数用于初始化未命名设备号的 ID 分配系统,基于 IDR(ID Radix Tree)机制来高效管理和分配设备号。
代码详细解释
第一部分:未命名设备初始化入口
void __init unnamed_dev_init(void)
{idr_init(&unnamed_dev_idr);
}
__init
:这个宏表示函数只在系统初始化期间使用,初始化完成后内存会被释放- 函数名:
unnamed_dev_init
- 未命名设备初始化 - 单行实现:
idr_init(&unnamed_dev_idr);
- 调用 IDR 初始化函数来初始化全局的
unnamed_dev_idr
结构 unnamed_dev_idr
是静态全局变量,用于管理未命名设备的 ID 分配
- 调用 IDR 初始化函数来初始化全局的
第二部分:全局变量声明
static struct idr unnamed_dev_idr;
static
:限制变量只在当前文件内可见struct idr
:IDR 数据结构,用于实现基数树(Radix Tree)的 ID 分配- 这个全局变量将用于管理所有未命名设备的设备号分配
第三部分:IDR 结构初始化
void idr_init(struct idr *idp)
{init_id_cache();memset(idp, 0, sizeof(struct idr));spin_lock_init(&idp->lock);
}
- 参数:
struct idr *idp
- 指向要初始化的 IDR 结构的指针
-
init_id_cache();
- 初始化 IDR 层缓存(slab 缓存)
- 确保有可用的内存来分配 IDR 树的节点
-
memset(idp, 0, sizeof(struct idr));
- 将整个
idr
结构体清零 - 确保所有字段都处于已知的初始状态
- 防止未初始化内存导致的问题
- 将整个
-
spin_lock_init(&idp->lock);
- 初始化自旋锁,用于保护对 IDR 结构的并发访问
- 确保多核环境下的线程安全
第四部分:IDR 层缓存初始化
static int init_id_cache(void)
{if (!idr_layer_cache)idr_layer_cache = kmem_cache_create("idr_layer_cache",sizeof(struct idr_layer), 0, 0, idr_cache_ctor, NULL);return 0;
}
-
static int
:静态函数,返回整型(总是返回 0) -
条件检查:
if (!idr_layer_cache)
- 检查全局变量
idr_layer_cache
是否已经初始化 - 如果为 NULL(未初始化),则创建 slab 缓存
- 这确保了缓存只被创建一次
- 检查全局变量
-
slab 缓存创建:
kmem_cache_create(...)
- 名称:
"idr_layer_cache"
- 标识这个缓存的名称 - 对象大小:
sizeof(struct idr_layer)
- IDR 树节点的大小 - 对齐:
0
- 使用默认对齐 - 标志:
0
- 无特殊标志 - 构造函数:
idr_cache_ctor
- 对象创建时的初始化函数 - 析构函数:
NULL
- 无特殊析构函数
- 名称:
-
返回值:
return 0;
- 总是成功返回
未命名设备的应用场景
- 临时设备:不需要持久设备名的设备
- 内部设备:内核内部使用的虚拟设备
- 动态设备:生命周期短暂的设备对象
- 匿名设备:不需要在 /dev 下创建节点的设备
初始化 Linux 安全框架security_init
int __init security_init(void)
{printk(KERN_INFO "Security Framework v" SECURITY_FRAMEWORK_VERSION" initialized\n");if (verify(&dummy_security_ops)) {printk(KERN_ERR "%s could not verify ""dummy_security_ops structure.\n", __FUNCTION__);return -EIO;}security_ops = &dummy_security_ops;do_security_initcalls();return 0;
}
static void __init do_security_initcalls(void)
{initcall_t *call;call = &__security_initcall_start;while (call < &__security_initcall_end) {(*call) ();call++;}
}
整体功能概述
这些函数用于初始化 Linux 安全框架(LSM - Linux Security Modules),建立系统安全子系统的基础设施
代码详细解释
第一部分:安全框架主初始化函数
int __init security_init(void)
{printk(KERN_INFO "Security Framework v" SECURITY_FRAMEWORK_VERSION" initialized\n");
int __init
:返回整型的初始化函数,__init
表示只在系统启动时运行- 版本信息打印:
printk(KERN_INFO ...)
:输出信息级内核日志SECURITY_FRAMEWORK_VERSION
:安全框架版本号宏- 通知用户安全框架已初始化完成
第二部分:虚拟安全操作验证
if (verify(&dummy_security_ops)) {printk(KERN_ERR "%s could not verify ""dummy_security_ops structure.\n", __FUNCTION__);return -EIO;}
-
验证检查:
if (verify(&dummy_security_ops))
verify()
函数检查dummy_security_ops
结构的完整性和有效性dummy_security_ops
是虚拟的安全操作结构,提供空操作实现
-
错误处理:
- 如果验证失败,输出错误日志:
KERN_ERR
表示错误级别 __FUNCTION__
宏展开为当前函数名 “security_init
”- 返回
-EIO
(I/O 错误)表示初始化失败
- 如果验证失败,输出错误日志:
第三部分:安全操作指针设置和初始化调用
security_ops = &dummy_security_ops;do_security_initcalls();return 0;
}
-
设置安全操作:
security_ops = &dummy_security_ops;
security_ops
是全局安全操作指针- 初始设置为虚拟操作,后续可能被具体的安全模块替换
-
执行初始化调用:
do_security_initcalls();
- 调用所有注册的安全模块初始化函数
-
成功返回:
return 0;
- 表示初始化成功完成
第四部分:安全初始化调用执行器
static void __init do_security_initcalls(void)
{initcall_t *call;call = &__security_initcall_start;while (call < &__security_initcall_end) {(*call) ();call++;}
}
-
函数签名:
static void __init
- 静态初始化函数,无返回值 -
变量声明:
initcall_t *call
:初始化调用函数指针initcall_t
通常是int (*)(void)
类型的函数指针
-
循环起点:
call = &__security_initcall_start;
__security_initcall_start
是链接器定义的符号- 指向安全初始化调用段的开始地址
-
循环执行:
while (call < &__security_initcall_end)
__security_initcall_end
指向段的结束地址- 循环遍历段中的所有初始化函数
-
函数调用:
(*call) ();
- 解引用函数指针并调用(无参数)
- 执行具体的安全模块初始化
-
指针递增:
call++;
- 移动到下一个函数指针
- 继续循环直到处理完所有初始化函数
链接器段的魔法
内存布局示意
.security.initcall 段
┌─────────────────────────┐
│ __security_initcall_start │ ← 段开始
├─────────────────────────┤
│ security_module1_init │ ← 第一个初始化函数指针
├─────────────────────────┤
│ security_module2_init │ ← 第二个初始化函数指针
├─────────────────────────┤
│ ... │
├─────────────────────────┤
│ security_moduleN_init │ ← 第N个初始化函数指针
├─────────────────────────┤
│ __security_initcall_end │ ← 段结束
└─────────────────────────┘
初始化VFS的各种缓存和数据结构vfs_caches_init
void __init vfs_caches_init(unsigned long mempages)
{unsigned long reserve;/* Base hash sizes on available memory, with a reserve equal to150% of current kernel size */reserve = min((mempages - nr_free_pages()) * 3/2, mempages - 1);mempages -= reserve;names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0,SLAB_HWCACHE_ALIGN|SLAB_PANIC, filp_ctor, filp_dtor);dcache_init(mempages);inode_init(mempages);files_init(mempages);mnt_init(mempages);bdev_cache_init();chrdev_init();
}
函数功能概述
vfs_caches_init
函数用于初始化虚拟文件系统(VFS)的各种缓存和数据结构,为文件系统操作提供基础设施。
代码详细解释
第一部分:函数签名和变量声明
void __init vfs_caches_init(unsigned long mempages)
{unsigned long reserve;
void __init
:无返回值的初始化函数,__init
表示只在系统启动时运行- 参数:
unsigned long mempages
- 系统总内存页数 - 局部变量:
unsigned long reserve
- 用于计算内存保留量
第二部分:内存保留计算
/* Base hash sizes on available memory, with a reserve equal to150% of current kernel size */reserve = min((mempages - nr_free_pages()) * 3/2, mempages - 1);mempages -= reserve;
-
基于可用内存计算哈希表大小,保留当前内核大小的150%
-
内存使用计算:
mempages - nr_free_pages()
nr_free_pages()
返回系统当前空闲页数mempages - nr_free_pages()
计算已使用的内存页数(主要是内核占用)
-
保留量计算:
(mempages - nr_free_pages()) * 3/2
- 已用内存的1.5倍(150%)
- 为内核运行保留足够的内存空间
-
安全限制:
min(..., mempages - 1)
- 确保保留量不超过总内存减1页
-
调整可用内存:
mempages -= reserve
- 从总内存中减去保留量
- 后续初始化基于调整后的内存量进行
第三部分:路径名缓存创建
names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
- 缓存名称:
"names_cache"
- 路径名缓存 - 对象大小:
PATH_MAX
- 最大路径长度(通常是4096字节) - 对齐:
0
- 默认对齐 - 标志:
SLAB_HWCACHE_ALIGN|SLAB_PANIC
- 缓存行对齐优化性能
- 创建失败时系统崩溃
- 构造函数/析构函数:
NULL, NULL
- 不需要特殊处理
第四部分:文件结构缓存创建
filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0,SLAB_HWCACHE_ALIGN|SLAB_PANIC, filp_ctor, filp_dtor);
- 缓存名称:
"filp"
- file结构缓存 - 对象大小:
sizeof(struct file)
- file结构体大小 - 标志:同样的性能和安全标志
- 构造函数:
filp_ctor
- file对象创建时初始化 - 析构函数:
filp_dtor
- file对象销毁时清理
第五部分:各子系统初始化
dcache_init(mempages);inode_init(mempages);files_init(mempages);mnt_init(mempages);bdev_cache_init();chrdev_init();
}
-
dcache_init(mempages)
- 目录项缓存初始化- 管理目录项(
dentry
)缓存,加速路径查找 - 基于内存大小调整哈希表尺寸
- 管理目录项(
-
inode_init(mempages)
-inode
缓存初始化- 管理
inode
缓存,文件元数据 - 初始化
inode
哈希表和slab缓存
- 管理
-
files_init(mempages)
- 文件描述符表初始化- 初始化文件描述符管理
- 设置最大文件打开数限制
-
mnt_init(mempages)
- 文件系统挂载点初始化- 管理文件系统挂载信息
- 初始化挂载点哈希表
-
bdev_cache_init()
- 块设备缓存初始化- 管理块设备特殊文件的缓存
- 无内存参数,使用固定大小
-
chrdev_init()
- 字符设备初始化- 管理字符设备注册和查找
- 无内存参数,初始化核心数据结构
创建新的 file 对象时进行初始化filp_ctor
void filp_ctor(void * objp, struct kmem_cache_s *cachep, unsigned long cflags)
{if ((cflags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==SLAB_CTOR_CONSTRUCTOR) {unsigned long flags;spin_lock_irqsave(&filp_count_lock, flags);files_stat.nr_files++;spin_unlock_irqrestore(&filp_count_lock, flags);}
}
函数功能概述
filp_ctor
是文件结构(struct file)的 slab 构造函数,用于在创建新的 file 对象时进行初始化,主要功能是统计全局文件打开数量。
代码详细解释
第一部分:函数签名和参数
void filp_ctor(void * objp, struct kmem_cache_s *cachep, unsigned long cflags)
- 返回值:
void
- 没有返回值 - 参数:
void *objp
:指向要构造的 slab 对象的指针(这里是 struct file)struct kmem_cache_s *cachep
:指向所属 slab 缓存的指针unsigned long cflags
:构造标志位,控制构造行为
第二部分:构造模式检查
if ((cflags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==SLAB_CTOR_CONSTRUCTOR) {
-
位掩码操作:
cflags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)
SLAB_CTOR_VERIFY
:验证模式标志(0x002)SLAB_CTOR_CONSTRUCTOR
:构造函数模式标志(0x001)- 按位与操作提取相关标志位
-
条件判断:检查是否处于纯构造函数模式
- 只有当
cflags
包含SLAB_CTOR_CONSTRUCTOR
但不包含SLAB_CTOR_VERIFY
时才执行 - 这确保只在真正创建新对象时更新计数,验证模式时跳过
- 只有当
第三部分:自旋锁获取
unsigned long flags;spin_lock_irqsave(&filp_count_lock, flags);
- 局部变量:
unsigned long flags
- 用于保存中断状态 - 中断安全的锁:
spin_lock_irqsave(&filp_count_lock, flags)
filp_count_lock
:保护文件计数的自旋锁spin_lock_irqsave
:在获取锁的同时禁用本地CPU中断,并保存中断状态- 为什么需要:防止中断处理程序与当前代码竞争导致死锁
第四部分:文件计数更新
files_stat.nr_files++;
- 全局统计:
files_stat
是全局文件统计结构 - 字段:
nr_files
- 当前系统分配的文件结构总数 - 递增操作:
files_stat.nr_files++
- 每次创建新的 struct file 时增加计数
- 用于监控系统文件打开情况和资源使用
第五部分:自旋锁释放
spin_unlock_irqrestore(&filp_count_lock, flags);}
- 锁释放:
spin_unlock_irqrestore(&filp_count_lock, flags)
- 释放保护文件计数的自旋锁
irqrestore
:恢复之前保存的中断状态,重新启用中断- 确保并发安全的同时不破坏中断处理
销毁 file 对象时进行清理filp_dtor
void filp_dtor(void * objp, struct kmem_cache_s *cachep, unsigned long dflags)
{unsigned long flags;spin_lock_irqsave(&filp_count_lock, flags);files_stat.nr_files--;spin_unlock_irqrestore(&filp_count_lock, flags);
}
函数功能概述
filp_dtor
是文件结构(struct file)的 slab 析构函数,用于在销毁 file 对象时进行清理,主要功能是递减全局文件打开数量统计
代码详细解释
第一部分:函数签名和参数
void filp_dtor(void * objp, struct kmem_cache_s *cachep, unsigned long dflags)
- 返回值:
void
- 没有返回值 - 参数:
void *objp
:指向要销毁的 slab 对象的指针(这里是 struct file)struct kmem_cache_s *cachep
:指向所属 slab 缓存的指针unsigned long dflags
:析构标志位,控制析构行为
第二部分:局部变量声明
unsigned long flags;
- 局部变量:
unsigned long flags
- 用于保存当前 CPU 的中断状态
- 在锁操作中传递中断状态信息
- 确保锁操作不会破坏系统的中断处理
第三部分:自旋锁获取
spin_lock_irqsave(&filp_count_lock, flags);
- 中断安全的锁获取:
spin_lock_irqsave(&filp_count_lock, flags)
&filp_count_lock
:指向保护文件计数的自旋锁的指针flags
:输出参数,保存当前中断状态- 操作语义:
- 禁用当前 CPU 的中断
- 保存中断使能状态到
flags
变量 - 获取自旋锁,如果锁被占用则自旋等待
第四部分:文件计数递减
files_stat.nr_files--;
- 全局统计更新:
files_stat.nr_files--
files_stat
:全局文件统计结构nr_files
:当前系统分配的文件结构总数- 递减操作:每次销毁 struct file 时减少计数
- 意义:准确反映系统当前实际使用的文件对象数量
第五部分:自旋锁释放
spin_unlock_irqrestore(&filp_count_lock, flags);
- 中断安全的锁释放:
spin_unlock_irqrestore(&filp_count_lock, flags)
&filp_count_lock
:要释放的自旋锁flags
:之前保存的中断状态- 操作语义:
- 释放自旋锁
- 根据
flags
恢复之前的中断状态(可能重新启用中断)
- 重要性:确保不破坏系统的中断处理流程