初始化目录项缓存dcache_init
c
static void __init dcache_init(unsigned long mempages)
{
/*
* A constructor could be added for stable state like the lists,
* but it is probably not worth it because of the cache nature
* of the dcache.
*/
dentry_cache = kmem_cache_create("dentry_cache",
sizeof(struct dentry),
0,
SLAB_RECLAIM_ACCOUNT|SLAB_PANIC,
NULL, NULL);
set_shrinker(DEFAULT_SEEKS, shrink_dcache_memory);
}
函数功能
dcache_init
函数用于初始化目录项缓存(dentry cache
)系统。它在内核启动时调用,创建dentry
对象的slab缓存并设置内存回收机制
代码逐段解释
第1行:函数声明
c
static void __init dcache_init(unsigned long mempages)
static void
: 静态函数,无返回值__init
: 表示该函数只在内核初始化阶段使用,初始化完成后内存会被释放- 参数 :
unsigned long mempages
- 系统总内存页数
第2-6行:注释说明
c
/*
* A constructor could be added for stable state like the lists,
* but it is probably not worth it because of the cache nature
* of the dcache.
*/
注释含义:
- 构造函数可能性 : 可以为
dentry
的稳定状态(如链表)添加构造函数 - 不值得的原因 : 由于
dcache
的缓存特性,添加构造函数可能不值得 - 深层含义 :
dentry
对象频繁创建和销毁,构造函数开销可能影响性能
第7-12行:创建dentry slab
缓存
c
dentry_cache = kmem_cache_create("dentry_cache",
sizeof(struct dentry),
0,
SLAB_RECLAIM_ACCOUNT|SLAB_PANIC,
NULL, NULL);
kmem_cache_create
参数详解:
"dentry_cache"
: slab缓存名称,用于调试和/proc/slabinfo
显示sizeof(struct dentry)
: 每个对象的大小0
: 对齐偏移量,0表示使用默认对齐SLAB_RECLAIM_ACCOUNT|SLAB_PANIC
: slab标志位NULL, NULL
: 构造函数和析构函数都为NULL
标志位说明:
SLAB_RECLAIM_ACCOUNT
: 标记该缓存为可回收内存,允许在内存压力时回收SLAB_PANIC
: 如果缓存创建失败,触发内核panic(系统崩溃)
第13行:设置内存收缩器
c
set_shrinker(DEFAULT_SEEKS, shrink_dcache_memory);
set_shrinker
: 注册内存收缩器函数DEFAULT_SEEKS
: 默认的查找代价参数,影响回收优先级shrink_dcache_memory
: 实际的收缩函数,在内存不足时被调用
内存回收触发
c
// 当系统内存紧张时:
// 1. kswapd或直接回收调用shrink_slab
// 2. shrink_slab调用注册的shrinker
// 3. shrink_dcache_memory被调用
// 4. 回收未使用的dentry对象
初始化inode
缓存系统inode_init
c
void __init inode_init(unsigned long mempages)
{
/* inode slab cache */
inode_cachep = kmem_cache_create("inode_cache", sizeof(struct inode),
0, SLAB_PANIC, init_once, NULL);
set_shrinker(DEFAULT_SEEKS, shrink_icache_memory);
}
函数功能
inode_init
函数用于初始化inode
缓存系统,它在内核启动时调用,创建inode
对象的slab缓存并设置内存回收机制
代码逐段解释
第1行:函数声明
c
void __init inode_init(unsigned long mempages)
void
: 无返回值__init
: 表示该函数只在内核初始化阶段使用,初始化完成后内存会被释放- 参数 :
unsigned long mempages
- 系统总内存页数(虽然函数中未直接使用)
第4-6行:创建inode slab
缓存
c
inode_cachep = kmem_cache_create("inode_cache", sizeof(struct inode),
0, SLAB_PANIC, init_once, NULL);
kmem_cache_create
参数详解:
"inode_cache"
: slab缓存名称,用于调试和/proc/slabinfo
显示sizeof(struct inode)
: 每个inode
对象的大小0
: 对齐偏移量,0表示使用默认对齐SLAB_PANIC
: slab标志位init_once
: 构造函数指针NULL
: 析构函数为NULL
关键参数说明:
init_once
:inode
对象的构造函数,在对象分配时调用进行初始化SLAB_PANIC
: 如果缓存创建失败,触发内核panic(系统崩溃)
第7行:设置内存收缩器
c
set_shrinker(DEFAULT_SEEKS, shrink_icache_memory);
set_shrinker
: 注册内存收缩器函数DEFAULT_SEEKS
: 默认的查找代价参数,影响回收优先级shrink_icache_memory
:inode
缓存的实际收缩函数,在内存不足时被调用
初始化文件描述符files_init
c
void __init files_init(unsigned long mempages)
{
int n;
/* One file with associated inode and dcache is very roughly 1K.
* Per default don't use more than 10% of our memory for files.
*/
n = (mempages * (PAGE_SIZE / 1024)) / 10;
files_stat.max_files = n;
if (files_stat.max_files < NR_FILE)
files_stat.max_files = NR_FILE;
}
函数功能
files_init
函数用于初始化文件描述符相关的系统参数,主要计算并设置系统允许的最大打开文件数量。它在内核启动时基于系统内存大小动态配置文件描述符限制
代码逐段解释
第1行:函数声明
c
void __init files_init(unsigned long mempages)
- 返回类型 :
void
- 无返回值 __init
: 表示该函数只在内核初始化阶段使用,初始化完成后内存会被释放- 参数 :
unsigned long mempages
- 系统总内存页数
第2-5行:注释说明
c
int n;
/* One file with associated inode and dcache is very roughly 1K.
* Per default don't use more than 10% of our memory for files.
*/
int n
: 声明临时变量,用于存储计算的最大文件数- 注释说明 :
- 一个文件及其关联的
inode
和dcache
大约占用1KB内存 - 默认情况下,不要使用超过10%的内存用于文件相关结构
- 一个文件及其关联的
第7行:计算最大文件数
c
n = (mempages * (PAGE_SIZE / 1024)) / 10;
计算步骤:
mempages
: 系统总内存页数PAGE_SIZE / 1024
: 将页面大小从字节转换为KB- 假设
PAGE_SIZE = 4096
字节 4096 / 1024 = 4
,表示每页4KB
- 假设
mempages * (PAGE_SIZE / 1024)
: 计算系统总内存的KB数- 例如:1000页 × 4KB/页 = 4000KB
/ 10
: 取总内存的10%作为文件相关结构的内存限制
第8行:设置最大文件数
c
files_stat.max_files = n;
files_stat.max_files
: 全局文件统计结构中的最大文件数字段- 将计算得到的值赋给系统全局变量
第9-10行:最小值保护
c
if (files_stat.max_files < NR_FILE)
files_stat.max_files = NR_FILE;
NR_FILE
: 编译时常量,文件数量的最小保证值- 逻辑: 如果计算的值小于最小保证值,使用最小保证值
- 目的: 确保系统至少有最基本数量的可用文件描述符
初始化挂载点相关的内核数据结构mnt_init
c
void __init mnt_init(unsigned long mempages)
{
struct list_head *d;
unsigned long order;
unsigned int nr_hash;
int i;
mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount),
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
order = 0;
mount_hashtable = (struct list_head *)
__get_free_pages(GFP_ATOMIC, order);
if (!mount_hashtable)
panic("Failed to allocate mount hash table\n");
/*
* Find the power-of-two list-heads that can fit into the allocation..
* We don't guarantee that "sizeof(struct list_head)" is necessarily
* a power-of-two.
*/
nr_hash = (1UL << order) * PAGE_SIZE / sizeof(struct list_head);
hash_bits = 0;
do {
hash_bits++;
} while ((nr_hash >> hash_bits) != 0);
hash_bits--;
/*
* Re-calculate the actual number of entries and the mask
* from the number of bits we can fit.
*/
nr_hash = 1UL << hash_bits;
hash_mask = nr_hash-1;
printk("Mount-cache hash table entries: %d (order: %ld, %ld bytes)\n",
nr_hash, order, (PAGE_SIZE << order));
/* And initialize the newly allocated array */
d = mount_hashtable;
i = nr_hash;
do {
INIT_LIST_HEAD(d);
d++;
i--;
} while (i);
sysfs_init();
init_rootfs();
init_mount_tree();
}
函数功能
mnt_init
函数用于初始化挂载点相关的内核数据结构,包括创建vfsmount
对象的slab缓存、分配和初始化挂载点哈希表,以及初始化根文件系统
代码逐段解释
第1行:函数声明
c
void __init mnt_init(unsigned long mempages)
- 返回类型 :
void
- 无返回值 __init
: 表示该函数只在内核初始化阶段使用- 参数 :
unsigned long mempages
- 系统总内存页数(虽然函数中未直接使用)
第2-5行:变量声明
c
struct list_head *d;
unsigned long order;
unsigned int nr_hash;
int i;
struct list_head *d
: 用于遍历哈希表数组的指针unsigned long order
: 内存分配阶数(2^order页)unsigned int nr_hash
: 哈希表条目数量int i
: 循环计数器
第7-8行:创建vfsmount slab
缓存
c
mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount),
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
kmem_cache_create
: 创建专用slab缓存- 参数 :
"mnt_cache"
: 缓存名称sizeof(struct vfsmount)
: 对象大小0
: 对齐偏移SLAB_HWCACHE_ALIGN|SLAB_PANIC
: 缓存标志NULL, NULL
: 无构造函数和析构函数
第10-12行:分配哈希表内存
c
order = 0;
mount_hashtable = (struct list_head *)
__get_free_pages(GFP_ATOMIC, order);
order = 0
: 分配阶数为0,即分配1页(通常4KB)__get_free_pages(GFP_ATOMIC, order)
: 分配连续物理页面GFP_ATOMIC
: 原子上下文分配,不会睡眠
第14-15行:内存分配失败检查
c
if (!mount_hashtable)
panic("Failed to allocate mount hash table\n");
- 如果内存分配失败,触发内核panic
第18-22行:计算哈希表容量
c
/*
* Find the power-of-two list-heads that can fit into the allocation..
* We don't guarantee that "sizeof(struct list_head)" is necessarily
* a power-of-two.
*/
nr_hash = (1UL << order) * PAGE_SIZE / sizeof(struct list_head);
- 计算 :
nr_hash = (1 << 0) * PAGE_SIZE / sizeof(struct list_head)
1 << 0
= 1页PAGE_SIZE
通常4096字节sizeof(struct list_head)
通常16字节(64位系统)nr_hash = 4096 / 16 = 256
第23-27行:计算哈希位数
c
hash_bits = 0;
do {
hash_bits++;
} while ((nr_hash >> hash_bits) != 0);
hash_bits--;
计算过程:
c
初始: hash_bits = 0, nr_hash = 256
循环1: hash_bits=1, 256>>1=128 ≠0 → 继续
循环2: hash_bits=2, 256>>2=64 ≠0 → 继续
...
循环8: hash_bits=8, 256>>8=1 ≠0 → 继续
循环9: hash_bits=9, 256>>9=0 → 退出循环
最后: hash_bits-- = 8
第32-34行:重新计算实际哈希表大小
c
nr_hash = 1UL << hash_bits;
hash_mask = nr_hash-1;
nr_hash = 1 << 8 = 256
: 实际哈希表条目数hash_mask = 256 - 1 = 255
: 哈希掩码(0xFF)
第36-38行:打印调试信息
c
printk("Mount-cache hash table entries: %d (order: %ld, %ld bytes)\n",
nr_hash, order, (PAGE_SIZE << order));
- 输出哈希表信息:256个条目,阶数0,4096字节
第41-47行:初始化哈希表
c
/* And initialize the newly allocated array */
d = mount_hashtable;
i = nr_hash;
do {
INIT_LIST_HEAD(d);
d++;
i--;
} while (i);
初始化过程:
c
d指向哈希表开始,i=256
循环256次:
初始化每个链表头(prev/next指向自己)
d指针向后移动
i计数器递减
第48-50行:后续初始化调用
c
sysfs_init();
init_rootfs();
init_mount_tree();
sysfs_init()
: 初始化sysfs
虚拟文件系统init_rootfs()
: 初始化根文件系统init_mount_tree()
: 初始化挂载树结构
详细机制分析
哈希表设计原理
挂载点哈希表 哈希桶0 哈希桶1 哈希桶2 ... 哈希桶255 链表头0 链表头1 链表头2 链表头255 vfsmount A vfsmount B vfsmount C 空链表 vfsmount D
哈希计算和查找
c
// 挂载点哈希函数通常基于:
hash = (dev ^ ino) & hash_mask;
// 查找过程:
1. 计算挂载点的哈希值
2. 找到对应的哈希桶
3. 遍历链表查找匹配的vfsmount
内存分配策略
c
// 为什么使用__get_free_pages而不是kmalloc?
1. 对齐要求: 页面分配保证自然对齐
2. 性能: 连续物理页面提高缓存效率
3. 大小: 需要分配较大的连续内存区域
4. 长期使用: 哈希表在内核生命周期一直存在
// GFP_ATOMIC标志:
- 在初始化早期,内存管理尚未完全建立
- 避免触发复杂的内存回收路径
- 确保分配能够立即完成
初始化sysfs
虚拟文件系统sysfs_init
c
int __init sysfs_init(void)
{
int err;
err = register_filesystem(&sysfs_fs_type);
if (!err) {
sysfs_mount = kern_mount(&sysfs_fs_type);
if (IS_ERR(sysfs_mount)) {
printk(KERN_ERR "sysfs: could not mount!\n");
err = PTR_ERR(sysfs_mount);
sysfs_mount = NULL;
}
}
return err;
}
函数功能
sysfs_init
函数用于初始化sysfs
虚拟文件系统。sysfs
是Linux内核中用于将内核对象导出到用户空间的重要机制,它提供了/sys目录下的层次化视图
代码逐段解释
第1行:函数声明
c
int __init sysfs_init(void)
- 返回类型 :
int
- 返回错误码,0表示成功 __init
: 表示该函数只在内核初始化阶段使用- 参数: 无参数
第2行:变量声明
c
int err;
int err
: 声明错误码变量,用于存储函数执行状态
第4-5行:注册文件系统类型
c
err = register_filesystem(&sysfs_fs_type);
if (!err) {
register_filesystem(&sysfs_fs_type)
: 向VFS注册sysfs
文件系统类型sysfs_fs_type
: 全局的sysfs
文件系统类型结构体
sysfs_fs_type
的典型定义:
c
static struct file_system_type sysfs_fs_type = {
.name = "sysfs",
.get_sb = sysfs_get_sb,
.kill_sb = kill_litter_super,
};
register_filesystem的作用:
c
// 将文件系统类型添加到全局文件系统链表
// 返回0表示成功,负数错误码表示失败
第6-11行:挂载sysfs
文件系统
c
sysfs_mount = kern_mount(&sysfs_fs_type);
if (IS_ERR(sysfs_mount)) {
printk(KERN_ERR "sysfs: could not mount!\n");
err = PTR_ERR(sysfs_mount);
sysfs_mount = NULL;
}
-
sysfs_mount = kern_mount(&sysfs_fs_type)
:- 内核内部挂载
sysfs
文件系统 - 返回
vfsmount
结构指针
- 内核内部挂载
-
if (IS_ERR(sysfs_mount))
:- 检查挂载是否失败
IS_ERR()
宏判断指针是否包含错误码
-
错误处理:
printk(KERN_ERR "sysfs: could not mount!\n")
: 打印错误消息err = PTR_ERR(sysfs_mount)
: 从指针中提取错误码sysfs_mount = NULL
: 清空挂载点指针
第12行:返回错误码
c
}
return err;
}
- 返回执行结果:0=成功,负数=错误码
sysfs
的重要性
sysfs
提供的功能
c
// sysfs的主要作用:
1. 设备模型导出: 将内核设备层次结构导出到用户空间
2. 动态属性: 允许运行时查看和修改设备属性
3. 热插拔事件: 支持设备热插拔通知
4. 驱动绑定: 管理设备与驱动的关联
// 典型的/sys目录结构:
/sys/devices/ # 系统所有设备
/sys/bus/ # 总线类型
/sys/class/ # 设备类别
/sys/block/ # 块设备
/sys/firmware/ # 固件接口
在内核初始化中的关键作用
sysfs_init 创建设备模型基础设施 为其他子系统提供导出接口 设备驱动依赖sysfs 电源管理依赖sysfs 热插拔依赖sysfs 用户空间工具依赖/sys 系统监控依赖sysfs 配置管理依赖sysfs
实际挂载结果
成功的初始化结果
c
// 成功后系统的状态:
1. sysfs_fs_type: 注册到全局文件系统列表
2. sysfs_mount: 指向有效的vfsmount结构
3. /sys目录: 可通过用户空间访问
4. 内核对象: 可以通过sysfs接口操作
用户空间可见的效果
bash
# 成功初始化后,用户可以看到:
$ ls /sys/
block bus class devices firmware kernel module power
# 每个目录对应内核对象的不同方面
初始化系统的根挂载树init_mount_tree
c
static void __init init_mount_tree(void)
{
struct vfsmount *mnt;
struct namespace *namespace;
struct task_struct *g, *p;
mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);
if (IS_ERR(mnt))
panic("Can't create rootfs");
namespace = kmalloc(sizeof(*namespace), GFP_KERNEL);
if (!namespace)
panic("Can't allocate initial namespace");
atomic_set(&namespace->count, 1);
INIT_LIST_HEAD(&namespace->list);
init_rwsem(&namespace->sem);
list_add(&mnt->mnt_list, &namespace->list);
namespace->root = mnt;
mnt->mnt_namespace = namespace;
init_task.namespace = namespace;
read_lock(&tasklist_lock);
do_each_thread(g, p) {
get_namespace(namespace);
p->namespace = namespace;
} while_each_thread(g, p);
read_unlock(&tasklist_lock);
set_fs_pwd(current->fs, namespace->root, namespace->root->mnt_root);
set_fs_root(current->fs, namespace->root, namespace->root->mnt_root);
}
函数功能
init_mount_tree
函数用于初始化系统的根挂载树,创建初始的挂载命名空间,并为所有进程设置根文件系统和当前工作目录。这是Linux内核启动过程中建立文件系统层次结构的关键步骤
代码逐段解释
第1行:函数声明
c
static void __init init_mount_tree(void)
static void
: 静态函数,无返回值__init
: 表示该函数只在内核初始化阶段使用- 参数: 无参数
第2-5行:变量声明
c
struct vfsmount *mnt;
struct namespace *namespace;
struct task_struct *g, *p;
struct vfsmount *mnt
: 挂载点结构指针struct namespace *namespace
: 挂载命名空间指针struct task_struct *g, *p
: 进程结构指针,用于遍历所有进程
第7-9行:创建根文件系统挂载点
c
mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);
if (IS_ERR(mnt))
panic("Can't create rootfs");
do_kern_mount("rootfs", 0, "rootfs", NULL)
: 内核内部挂载rootfs
- 第一个"
rootfs
": 文件系统类型 0
: 挂载标志- 第二个"
rootfs
": 设备名 NULL
: 额外数据
- 第一个"
IS_ERR(mnt)
: 检查挂载是否失败panic("Can't create rootfs")
: 如果失败,系统崩溃
第10-12行:分配命名空间内存
c
namespace = kmalloc(sizeof(*namespace), GFP_KERNEL);
if (!namespace)
panic("Can't allocate initial namespace");
kmalloc(sizeof(*namespace), GFP_KERNEL)
: 分配命名空间结构内存if (!namespace)
: 检查内存分配是否成功panic("Can't allocate initial namespace")
: 如果失败,系统崩溃
第13-16行:初始化命名空间
c
atomic_set(&namespace->count, 1);
INIT_LIST_HEAD(&namespace->list);
init_rwsem(&namespace->sem);
list_add(&mnt->mnt_list, &namespace->list);
atomic_set(&namespace->count, 1)
: 设置引用计数为1INIT_LIST_HEAD(&namespace->list)
: 初始化挂载点链表头init_rwsem(&namespace->sem)
: 初始化读写信号量,保护命名空间list_add(&mnt->mnt_list, &namespace->list)
: 将根挂载点添加到命名空间链表
第17-18行:设置命名空间的根挂载点
c
namespace->root = mnt;
mnt->mnt_namespace = namespace;
namespace->root = mnt
: 设置命名空间的根挂载点mnt->mnt_namespace = namespace
: 设置挂载点所属的命名空间
第20行:设置init
进程的命名空间
c
init_task.namespace = namespace;
init_task
: 内核的初始进程(pid
=1)- 设置
init
进程使用新创建的命名空间
第21-27行:遍历所有进程设置命名空间
c
read_lock(&tasklist_lock);
do_each_thread(g, p) {
get_namespace(namespace);
p->namespace = namespace;
} while_each_thread(g, p);
read_unlock(&tasklist_lock);
read_lock(&tasklist_lock)
: 获取进程列表读锁do_each_thread(g, p)
: 遍历所有进程的宏g
: 进程组指针p
: 当前进程指针
get_namespace(namespace)
: 增加命名空间引用计数p->namespace = namespace
: 设置进程的命名空间while_each_thread(g, p)
: 循环结束宏read_unlock(&tasklist_lock)
: 释放进程列表锁
第29-30行:设置当前进程的工作目录和根目录
c
set_fs_pwd(current->fs, namespace->root, namespace->root->mnt_root);
set_fs_root(current->fs, namespace->root, namespace->root->mnt_root);
set_fs_pwd()
: 设置当前工作目录current->fs
: 当前进程的文件系统状态namespace->root
: 根挂载点namespace->root->mnt_root
: 根目录的dentry
set_fs_root()
: 设置根目录- 参数同上
初始化块设备缓存系统bdev_cache_init
c
void __init bdev_cache_init(void)
{
int err;
bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode),
0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_PANIC,
init_once, NULL);
err = register_filesystem(&bd_type);
if (err)
panic("Cannot register bdev pseudo-fs");
bd_mnt = kern_mount(&bd_type);
err = PTR_ERR(bd_mnt);
if (IS_ERR(bd_mnt))
panic("Cannot create bdev pseudo-fs");
blockdev_superblock = bd_mnt->mnt_sb; /* For writeback */
}
函数功能概述
bdev_cache_init
函数用于初始化块设备缓存系统,创建块设备的inode
缓存、注册伪文件系统并挂载,为后续块设备操作提供基础设施
代码逐段详解
1. 创建块设备缓存
c
bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode),
0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_PANIC,
init_once, NULL);
kmem_cache_create
: 创建专用的SLAB分配器缓存- "
bdev_cache
": 缓存名称,用于调试和统计 sizeof(struct bdev_inode)
: 每个缓存对象的大小- 标志位说明 :
SLAB_HWCACHE_ALIGN
: 硬件缓存行对齐,提高缓存性能SLAB_RECLAIM_ACCOUNT
: 允许内存回收时统计该缓存SLAB_PANIC
: 如果创建失败则系统panic
init_once
: 对象构造函数,在对象分配时调用进行初始化- NULL: 析构函数参数,这里不需要
2. 注册块设备文件系统
c
err = register_filesystem(&bd_type);
if (err)
panic("Cannot register bdev pseudo-fs");
- register_filesystem: 向内核注册文件系统类型
- &bd_type: 指向块设备伪文件系统描述符的指针
- 错误处理: 如果注册失败(err≠0),系统panic,因为块设备是系统核心功能
3. 挂载块设备文件系统
c
bd_mnt = kern_mount(&bd_type);
err = PTR_ERR(bd_mnt);
if (IS_ERR(bd_mnt))
panic("Cannot create bdev pseudo-fs");
- kern_mount: 内核内部挂载文件系统,不涉及用户空间
- PTR_ERR: 将可能错误的指针转换为错误码
- IS_ERR: 检查指针是否为错误指针
- 错误处理: 挂载失败时系统panic
4. 设置回写超级块
c
blockdev_superblock = bd_mnt->mnt_sb; /* For writeback */
blockdev_superblock
: 全局变量,存储块设备超级块指针mnt_sb
: 从挂载点获取超级块结构- 用途: 为块设备的回写(writeback)操作提供超级块引用
分配新的bdev_inode
对象时进行初始化init_once
c
static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
{
struct bdev_inode *ei = (struct bdev_inode *) foo;
struct block_device *bdev = &ei->bdev;
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
SLAB_CTOR_CONSTRUCTOR)
{
memset(bdev, 0, sizeof(*bdev));
sema_init(&bdev->bd_sem, 1);
sema_init(&bdev->bd_mount_sem, 1);
INIT_LIST_HEAD(&bdev->bd_inodes);
INIT_LIST_HEAD(&bdev->bd_list);
inode_init_once(&ei->vfs_inode);
}
}
函数功能
init_once
函数是Linux内核中块设备inode
的slab缓存构造函数,用于在分配新的bdev_inode
对象时进行初始化。它确保了每个块设备inode
在首次使用时处于一致和正确的状态
代码逐段解释
第1行:函数声明
c
static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
static void
: 静态函数,无返回值- 参数 :
void *foo
: 要初始化的内存块指针(泛型指针)kmem_cache_t *cachep
: slab缓存指针unsigned long flags
: 构造标志位
第2-3行:类型转换
c
struct bdev_inode *ei = (struct bdev_inode *) foo;
struct block_device *bdev = &ei->bdev;
struct bdev_inode *ei = (struct bdev_inode *)foo
:- 将泛型指针转换为具体的
bdev_inode
结构体指针 bdev_inode
是包含块设备和VFSinode
的复合结构
- 将泛型指针转换为具体的
struct block_device *bdev = &ei->bdev
:- 获取内嵌的块设备结构体指针
bdev
是ei
中的第一个成员
第4-6行:构造标志检查
c
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
SLAB_CTOR_CONSTRUCTOR)
{
SLAB_CTOR_CONSTRUCTOR
: 表示这是构造函数调用(正常初始化)SLAB_CTOR_VERIFY
: 表示这是验证调用(调试用途)flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)
: 提取相关标志位- 条件: 只有当是构造函数调用且不是验证调用时才执行初始化
第7-8行:块设备内存清零和信号量初始化
c
memset(bdev, 0, sizeof(*bdev));
sema_init(&bdev->bd_sem, 1);
sema_init(&bdev->bd_mount_sem, 1);
memset(bdev, 0, sizeof(*bdev))
: 将整个块设备结构体清零sema_init(&bdev->bd_sem, 1)
: 初始化块设备信号量为1(可用状态)- 用于保护块设备的并发访问
sema_init(&bdev->bd_mount_sem, 1)
: 初始化挂载信号量为1- 用于序列化文件系统挂载操作
第9-10行:初始化链表头
c
INIT_LIST_HEAD(&bdev->bd_inodes);
INIT_LIST_HEAD(&bdev->bd_list);
INIT_LIST_HEAD(&bdev->bd_inodes)
: 初始化inode
链表头- 用于链接所有指向此块设备的
inode
- 用于链接所有指向此块设备的
INIT_LIST_HEAD(&bdev->bd_list)
: 初始化块设备链表头- 用于将块设备链接到全局块设备列表中
第11行:初始化VFS inode
c
inode_init_once(&ei->vfs_inode);
inode_init_once(&ei->vfs_inode)
: 调用VFSinode
的初始化函数- 初始化
inode
的各种字段:引用计数、链表、锁等 - 设置
inode
的初始状态
- 初始化
注册一个新的文件系统类型register_filesystem
c
int register_filesystem(struct file_system_type * fs)
{
int res = 0;
struct file_system_type ** p;
if (!fs)
return -EINVAL;
if (fs->next)
return -EBUSY;
INIT_LIST_HEAD(&fs->fs_supers);
write_lock(&file_systems_lock);
p = find_filesystem(fs->name);
if (*p)
res = -EBUSY;
else
*p = fs;
write_unlock(&file_systems_lock);
return res;
}
函数功能概述
register_filesystem
函数用于向内核注册一个新的文件系统类型,使其可以被挂载和使用。它确保文件系统名称唯一,并将文件系统添加到全局文件系统列表中
代码逐段详解
1. 参数检查和变量声明
c
int res = 0;
struct file_system_type ** p;
if (!fs)
return -EINVAL;
if (fs->next)
return -EBUSY;
- res = 0: 初始化返回值为0(成功)
- p: 用于在文件系统链表中查找位置的二级指针
- !fs检查 : 如果传入的fs指针为NULL,返回
-EINVAL
(无效参数错误) - fs->next检查 : 如果fs->next不为NULL,说明该文件系统已经注册过了,返回
-EBUSY
(设备或资源忙)
2. 初始化超级块列表
c
INIT_LIST_HEAD(&fs->fs_supers);
- INIT_LIST_HEAD: 初始化链表头,用于管理该文件系统的所有超级块
- fs_supers : 是
struct file_system_type
中的一个链表头,用于链接所有使用该文件系统类型的超级块
3. 加锁和查找文件系统
c
write_lock(&file_systems_lock);
p = find_filesystem(fs->name);
- write_lock: 获取文件系统列表的写锁,防止并发修改
- file_systems_lock: 保护全局文件系统列表的读写锁
- find_filesystem: 根据文件系统名称在全局列表中查找,返回指向该位置指针的地址
4. 检查并注册文件系统
c
if (*p)
res = -EBUSY;
else
*p = fs;
- *p检查 : 如果find_filesystem返回的位置已经有文件系统存在(*p ≠ NULL)
- 设置res = -EBUSY,表示文件系统名称冲突
- else分支 : 如果该位置为空(*p == NULL)
- 将新的文件系统指针fs赋值给*p,完成注册
5. 释放锁并返回
c
write_unlock(&file_systems_lock);
return res;
- write_unlock: 释放文件系统列表的写锁
- return res: 返回操作结果(0表示成功,负数表示错误)
查找指定名称的文件系统类型find_filesystem
c
static struct file_system_type **find_filesystem(const char *name)
{
struct file_system_type **p;
for (p=&file_systems; *p; p=&(*p)->next)
if (strcmp((*p)->name,name) == 0)
break;
return p;
}
函数功能概述
find_filesystem
函数用于在全局文件系统链表中查找指定名称的文件系统类型。它返回一个指向指针的指针,这个指针指向找到的文件系统节点或插入位置
代码逐段详解
1. 变量声明和初始化
c
struct file_system_type **p;
for (p=&file_systems; *p; p=&(*p)->next)
- p: 二级指针,指向文件系统链表中的指针(可以是节点指针或next字段的地址)
- p=&file_systems: 初始化p指向全局文件系统链表的头指针
- p: 循环条件,当p不为NULL时继续循环(即当前节点存在)
- *p=&(p)->next: 每次迭代后,p指向当前节点的next字段的地址
2. 名称比较和循环控制
c
if (strcmp((*p)->name,name) == 0)
break;
strcmp((*p)->name,name)
: 比较当前文件系统节点的名称与目标名称- == 0 : 如果
strcmp
返回0,表示名称完全匹配 - break: 找到匹配的文件系统,立即跳出循环
3. 返回结果
c
return p;
- 返回当前p的值,即指向找到的文件系统节点指针的地址,或者指向链表末尾NULL指针的地址
二级指针的巧妙之处
这种使用二级指针的设计有多个优点:
- 统一处理: 无论查找成功还是失败,都返回相同类型的值
- 插入方便 : 调用者可以直接通过
*p = new_fs
来插入新节点 - 头指针处理: 即使要操作链表头指针,也能统一处理