Linux中VFS相关slab缓存对象的创建和初始化

初始化目录项缓存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: 声明临时变量,用于存储计算的最大文件数
  • 注释说明 :
    • 一个文件及其关联的inodedcache大约占用1KB内存
    • 默认情况下,不要使用超过10%的内存用于文件相关结构

第7行:计算最大文件数

c 复制代码
        n = (mempages * (PAGE_SIZE / 1024)) / 10;

计算步骤:

  1. mempages: 系统总内存页数
  2. PAGE_SIZE / 1024 : 将页面大小从字节转换为KB
    • 假设PAGE_SIZE = 4096字节
    • 4096 / 1024 = 4,表示每页4KB
  3. mempages * (PAGE_SIZE / 1024) : 计算系统总内存的KB数
    • 例如:1000页 × 4KB/页 = 4000KB
  4. / 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;
                }
  1. sysfs_mount = kern_mount(&sysfs_fs_type):

    • 内核内部挂载sysfs文件系统
    • 返回vfsmount结构指针
  2. if (IS_ERR(sysfs_mount)):

    • 检查挂载是否失败
    • IS_ERR()宏判断指针是否包含错误码
  3. 错误处理:

    • 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);
  1. atomic_set(&namespace->count, 1): 设置引用计数为1
  2. INIT_LIST_HEAD(&namespace->list): 初始化挂载点链表头
  3. init_rwsem(&namespace->sem): 初始化读写信号量,保护命名空间
  4. 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);
  1. read_lock(&tasklist_lock): 获取进程列表读锁
  2. do_each_thread(g, p) : 遍历所有进程的宏
    • g: 进程组指针
    • p: 当前进程指针
  3. get_namespace(namespace): 增加命名空间引用计数
  4. p->namespace = namespace: 设置进程的命名空间
  5. while_each_thread(g, p): 循环结束宏
  6. 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是包含块设备和VFS inode的复合结构
  • struct block_device *bdev = &ei->bdev :
    • 获取内嵌的块设备结构体指针
    • bdevei中的第一个成员

第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) : 调用VFS inode的初始化函数
    • 初始化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指针的地址

二级指针的巧妙之处

这种使用二级指针的设计有多个优点:

  1. 统一处理: 无论查找成功还是失败,都返回相同类型的值
  2. 插入方便 : 调用者可以直接通过*p = new_fs来插入新节点
  3. 头指针处理: 即使要操作链表头指针,也能统一处理
相关推荐
fxshy3 小时前
CentOS 7上安装并配置Nginx监听81端口的完整指南
linux·nginx·centos
小熊熊知识库4 小时前
Ubuntu下载以及安装详解以及应用安装
linux·运维·ubuntu
小白银子8 小时前
零基础从头教学Linux(Day 52)
linux·运维·服务器·python·python3.11
平生不喜凡桃李10 小时前
Linux网络:UDP
linux·网络·udp
weixiao043010 小时前
Linux网络 网络层
linux·网络·智能路由器
从零开始的ops生活11 小时前
【Day 80】Linux-NAS 和 SAN 存储
linux·运维·php
Wang's Blog12 小时前
Linux小课堂: 输入重定向与管道操作详解
linux·运维·服务器
迎風吹頭髮12 小时前
Linux内核架构浅谈49-Linux per-CPU页面缓存:热页与冷页的管理与调度优化
linux·缓存·架构
jason.zeng@150220712 小时前
centos中安装redis
linux·redis·centos