当前位置: 首页 > news >正文

Linux中挂载文件系统函数的实现

挂载文件系统的步骤总结及流程图

在 Linux 内核中,挂载文件系统是一个复杂的过程,涉及超级块(super_block)、挂载点(vfsmount)、dentryinode 的初始化与管理。以下是挂载文件系统的核心步骤及流程图:

挂载文件系统的核心步骤

获取文件系统类型 (get_fs_type)

  • 目的:根据文件系统名称(如 ext4bdev)查找对应的 file_system_type 结构
  • 流程
    1. 调用 get_fs_type(name) 查找已注册的文件系统
    2. 如果未找到,尝试动态加载模块(request_module
    3. 增加文件系统模块的引用计数(try_module_get

分配并初始化 vfsmount 结构 (alloc_vfsmnt)

  • 目的:为挂载点分配内存并初始化链表和引用计数。
  • 流程
    1. 调用 kmem_cache_allocmnt_cache 分配内存
    2. 初始化 vfsmount 的链表头(mnt_hashmnt_child 等)
    3. 设置引用计数(mnt_count)和设备名称(mnt_devname

获取或创建超级块 (sget)

  • 目的:查找或创建超级块,并初始化其基本属性
  • 流程
    1. 调用 sget 查找匹配的超级块(通过 test 函数比较)
    2. 如果未找到,调用 alloc_super 分配新的超级块
    3. 初始化超级块的操作函数(s_op)、同步机制(信号量)和引用计数

调用文件系统特定的 get_sb 方法

  • 目的:由文件系统实现(如 ext4_get_sb)初始化超级块的具体内容
  • 流程
    1. 调用 type->get_sb(如 bd_get_sb)创建伪文件系统超级块
    2. 设置超级块的块大小、魔数、操作函数等

初始化根 dentryinode

  • 目的:为挂载点创建根目录的 dentryinode
  • 流程
    1. 调用 new_inode 创建根 inode,设置权限(S_IFDIR)和时间戳
    2. 调用 d_alloc 创建根 dentry,并将其与 inode 关联(d_instantiate
    3. 设置超级块的根 dentrys->s_root

安全检查和激活超级块

  • 目的:确保挂载操作符合安全策略,并标记超级块为活跃状态
  • 流程
    1. 调用 security_sb_kern_mount 进行安全检查
    2. 设置超级块标志(MS_ACTIVE
    3. 释放写锁(up_write)并返回 vfsmount

错误处理和资源释放

  • 目的:在失败时清理已分配的资源
  • 流程
    1. 释放 vfsmountfree_vfsmnt
    2. 停用超级块(deactivate_super
    3. 减少文件系统模块的引用计数(put_filesystem

挂载文件系统的流程图

成功
失败
找到现有超级块
创建新超级块
成功
失败
开始挂载
get_fs_type: 查找文件系统类型
alloc_vfsmnt: 分配vfsmount
request_module: 动态加载模块
sget: 查找或创建超级块
grab_super: 增加引用计数
alloc_super: 分配超级块
调用type->get_sb初始化超级块
new_inode: 创建根inode
d_alloc: 创建根dentry
d_instantiate: 关联dentry和inode
security_sb_kern_mount: 安全检查
设置MS_ACTIVE并返回vfsmount
deactivate_super: 清理资源
挂载完成
释放vfsmount和超级块

总结

挂载文件系统的核心流程包括:

  1. 查找文件系统类型get_fs_type)。
  2. 分配挂载点结构alloc_vfsmnt)。
  3. 获取或创建超级块sget + alloc_super)。
  4. 初始化根目录的 inodedentry
  5. 安全检查并激活超级块
  6. 错误处理和资源释放

在内核内部挂载文件系统kern_mount

struct vfsmount *kern_mount(struct file_system_type *type)
{return do_kern_mount(type->name, 0, type->name, NULL);
}
struct vfsmount *
do_kern_mount(const char *fstype, int flags, const char *name, void *data)
{struct file_system_type *type = get_fs_type(fstype);struct super_block *sb = ERR_PTR(-ENOMEM);struct vfsmount *mnt;int error;char *secdata = NULL;if (!type)return ERR_PTR(-ENODEV);mnt = alloc_vfsmnt(name);if (!mnt)goto out;if (data) {secdata = alloc_secdata();if (!secdata) {sb = ERR_PTR(-ENOMEM);goto out_mnt;}error = security_sb_copy_data(type, data, secdata);if (error) {sb = ERR_PTR(error);goto out_free_secdata;}}sb = type->get_sb(type, flags, name, data);if (IS_ERR(sb))goto out_free_secdata;error = security_sb_kern_mount(sb, secdata);if (error)goto out_sb;mnt->mnt_sb = sb;mnt->mnt_root = dget(sb->s_root);mnt->mnt_mountpoint = sb->s_root;mnt->mnt_parent = mnt;mnt->mnt_namespace = current->namespace;up_write(&sb->s_umount);put_filesystem(type);return mnt;
out_sb:up_write(&sb->s_umount);deactivate_super(sb);sb = ERR_PTR(error);
out_free_secdata:free_secdata(secdata);
out_mnt:free_vfsmnt(mnt);
out:put_filesystem(type);return (struct vfsmount *)sb;
}

函数功能概述

这两个函数用于在内核内部挂载文件系统,不涉及用户空间。它们创建并初始化一个vfsmount结构,为文件系统提供挂载点基础设施

kern_mount 函数

struct vfsmount *kern_mount(struct file_system_type *type)
{return do_kern_mount(type->name, 0, type->name, NULL);
}
  • 参数: 接收文件系统类型指针
  • 调用do_kern_mount:
    • type->name: 文件系统名称作为fstype参数
    • 0: 挂载标志位为0
    • type->name: 再次作为名称参数
    • NULL: 没有额外的挂载数据

do_kern_mount 函数详解

1. 变量声明和初始化

struct file_system_type *type = get_fs_type(fstype);
struct super_block *sb = ERR_PTR(-ENOMEM);
struct vfsmount *mnt;
int error;
char *secdata = NULL;
  • type: 通过名称获取文件系统类型结构
  • sb: 初始化超级块指针为内存不足错误
  • mnt: 将要分配的vfsmount结构
  • error: 错误码存储
  • secdata: 安全数据指针,初始为NULL

2. 文件系统类型检查

if (!type)return ERR_PTR(-ENODEV);
  • 如果get_fs_type返回NULL,表示找不到该文件系统类型
  • 返回-ENODEV(设备不存在)错误

3. 分配vfsmount结构

mnt = alloc_vfsmnt(name);
if (!mnt)goto out;
  • alloc_vfsmnt: 分配并初始化vfsmount结构
  • 如果分配失败,跳转到out标签进行清理

4. 安全数据处理

if (data) {secdata = alloc_secdata();if (!secdata) {sb = ERR_PTR(-ENOMEM);goto out_mnt;}error = security_sb_copy_data(type, data, secdata);if (error) {sb = ERR_PTR(error);goto out_free_secdata;}
}
  • data存在检查: 如果有挂载数据需要处理
  • alloc_secdata: 分配安全数据内存
  • 内存分配失败: 跳转到out_mnt清理vfsmount
  • security_sb_copy_data: 复制并验证安全数据
  • 安全数据错误: 跳转到out_free_secdata清理安全数据

5. 获取超级块

sb = type->get_sb(type, flags, name, data);
if (IS_ERR(sb))goto out_free_secdata;
  • type->get_sb: 调用文件系统特定的获取超级块方法
  • IS_ERR检查: 如果获取超级块失败,跳转到out_free_secdata

6. 安全挂载检查

error = security_sb_kern_mount(sb, secdata);
if (error)goto out_sb;
  • security_sb_kern_mount: 安全检查,确认允许内核挂载
  • 检查失败: 跳转到out_sb清理超级块

7. 初始化vfsmount结构

mnt->mnt_sb = sb;
mnt->mnt_root = dget(sb->s_root);
mnt->mnt_mountpoint = sb->s_root;
mnt->mnt_parent = mnt;
mnt->mnt_namespace = current->namespace;
  • mnt_sb: 设置vfsmount的超级块指针
  • mnt_root: 获取并增加根目录dentry的引用计数
  • mnt_mountpoint: 设置挂载点为超级块的根目录
  • mnt_parent: 设置父挂载点为自身(这是根挂载点)
  • mnt_namespace: 设置命名空间为当前进程的命名空间

8. 完成挂载

up_write(&sb->s_umount);
put_filesystem(type);
return mnt;
  • up_write: 释放超级块的写锁
  • put_filesystem: 减少文件系统类型的引用计数
  • return mnt: 返回成功创建的vfsmount结构

9. 错误处理路径

out_sb 标签:

out_sb:up_write(&sb->s_umount);deactivate_super(sb);sb = ERR_PTR(error);
  • 释放超级块锁
  • 停用超级块
  • 设置错误指针

out_free_secdata 标签:

out_free_secdata:free_secdata(secdata);
  • 释放安全数据内存

out_mnt 标签:

out_mnt:free_vfsmnt(mnt);
  • 释放vfsmount结构

out 标签:

out:put_filesystem(type);return (struct vfsmount *)sb;
  • 减少文件系统类型引用计数
  • 返回错误指针

根据名称获取文件系统类型结构get_fs_type

struct file_system_type *get_fs_type(const char *name)
{struct file_system_type *fs;read_lock(&file_systems_lock);fs = *(find_filesystem(name));if (fs && !try_module_get(fs->owner))fs = NULL;read_unlock(&file_systems_lock);if (!fs && (request_module("%s", name) == 0)) {read_lock(&file_systems_lock);fs = *(find_filesystem(name));if (fs && !try_module_get(fs->owner))fs = NULL;read_unlock(&file_systems_lock);}return fs;
}

函数功能概述

get_fs_type 函数用于根据名称获取文件系统类型结构,并增加其模块引用计数。如果文件系统未加载,它会尝试动态加载该文件系统模块

代码逐段详解

1. 变量声明和第一次查找

struct file_system_type *fs;read_lock(&file_systems_lock);
fs = *(find_filesystem(name));
  • fs: 文件系统类型指针,用于存储查找结果
  • read_lock: 获取文件系统列表的读锁,允许并发读取
  • find_filesystem(name): 调用查找函数,返回指向指针的指针
  • *(find_filesystem(name)): 解引用,获取实际的文件系统类型指针

2. 模块引用计数管理

if (fs && !try_module_get(fs->owner))fs = NULL;
  • fs检查: 如果找到了文件系统类型(fs不为NULL)
  • try_module_get(fs->owner): 尝试增加文件系统模块的引用计数
    • 成功:返回非零,保持fs不变
    • 失败:返回0,表示模块正在被卸载或其他问题
  • fs = NULL: 如果增加引用计数失败,将fs设为NULL

3. 释放锁和第一次查找结果检查

read_unlock(&file_systems_lock);
if (!fs && (request_module("%s", name) == 0)) {
  • read_unlock: 释放读锁
  • !fs检查: 如果第一次没有找到文件系统(fs为NULL)
  • request_module(“%s”, name): 请求加载名为name的内核模块
    • 返回0表示模块加载请求成功(不一定立即加载完成)
  • 条件成立: 如果文件系统不存在但模块加载请求成功

4. 第二次查找(模块加载后)

        read_lock(&file_systems_lock);fs = *(find_filesystem(name));if (fs && !try_module_get(fs->owner))fs = NULL;read_unlock(&file_systems_lock);
  • 重新加锁: 再次获取读锁
  • 重新查找: 再次在文件系统列表中查找同名文件系统
  • 再次尝试引用: 如果找到,再次尝试增加模块引用计数
  • 释放锁: 完成操作后释放读锁

5. 返回结果

return fs;
  • 返回找到的文件系统类型指针,或者NULL(如果未找到或引用失败)

vfsmount分配并初始化 alloc_vfsmnt

struct vfsmount *alloc_vfsmnt(const char *name)
{struct vfsmount *mnt = kmem_cache_alloc(mnt_cache, GFP_KERNEL);if (mnt) {memset(mnt, 0, sizeof(struct vfsmount));atomic_set(&mnt->mnt_count,1);INIT_LIST_HEAD(&mnt->mnt_hash);INIT_LIST_HEAD(&mnt->mnt_child);INIT_LIST_HEAD(&mnt->mnt_mounts);INIT_LIST_HEAD(&mnt->mnt_list);INIT_LIST_HEAD(&mnt->mnt_fslink);if (name) {int size = strlen(name)+1;char *newname = kmalloc(size, GFP_KERNEL);if (newname) {memcpy(newname, name, size);mnt->mnt_devname = newname;}}}return mnt;
}

函数功能

alloc_vfsmnt函数用于分配并初始化一个虚拟文件系统挂载点结构(vfsmount)。它是Linux内核文件系统挂载机制的基础函数,负责创建和管理文件系统挂载点的内核数据结构

代码逐段解释

第1行:函数声明

struct vfsmount *alloc_vfsmnt(const char *name)
  • 返回类型: struct vfsmount * - 指向vfsmount结构的指针
  • 参数: const char *name - 挂载设备名称(可为NULL)
  • 功能: 分配并初始化一个新的vfsmount结构

第2-3行:内存分配

        struct vfsmount *mnt = kmem_cache_alloc(mnt_cache, GFP_KERNEL);if (mnt) {
  • kmem_cache_alloc(mnt_cache, GFP_KERNEL):
    • 从专用的slab缓存mnt_cache中分配内存
    • mnt_cache: 预创建的vfsmount对象缓存,提高分配效率
    • GFP_KERNEL: 标准内核内存分配标志
  • if (mnt): 检查内存分配是否成功
    • 如果分配失败,mnt为NULL,直接返回NULL

第4行:内存清零

                memset(mnt, 0, sizeof(struct vfsmount));
  • memset(mnt, 0, sizeof(struct vfsmount)):
    • 将整个vfsmount结构体清零
    • 确保所有字段都处于已知的初始状态
    • 避免未初始化内存导致的问题

第5行:设置引用计数

                atomic_set(&mnt->mnt_count,1);
  • atomic_set(&mnt->mnt_count,1):
    • 原子性地设置引用计数为1
    • mnt_count: 记录该挂载点被引用的次数
    • 初始值为1表示创建者持有第一个引用

第6-10行:初始化链表头

                INIT_LIST_HEAD(&mnt->mnt_hash);INIT_LIST_HEAD(&mnt->mnt_child);INIT_LIST_HEAD(&mnt->mnt_mounts);INIT_LIST_HEAD(&mnt->mnt_list);INIT_LIST_HEAD(&mnt->mnt_fslink);

各个链表的用途:

  • mnt_hash: 用于哈希表,快速查找挂载点
  • mnt_child: 在同一父挂载点下的兄弟挂载点链表
  • mnt_mounts: 该挂载点的子挂载点链表
  • mnt_list: 全局挂载点链表
  • mnt_fslink: 文件系统链接链表

INIT_LIST_HEAD宏:

// 将链表头的prev和next指针都指向自己
// 创建空的双向循环链表

第11-18行:设置设备名称

                if (name) {int size = strlen(name)+1;char *newname = kmalloc(size, GFP_KERNEL);if (newname) {memcpy(newname, name, size);mnt->mnt_devname = newname;}}
  1. if (name): 检查是否提供了设备名称
  2. int size = strlen(name)+1: 计算名称长度(包含终止符)
  3. char *newname = kmalloc(size, GFP_KERNEL): 为名称分配内存
  4. if (newname): 检查名称内存分配是否成功
  5. memcpy(newname, name, size): 复制设备名称(包括’\0’)
  6. mnt->mnt_devname = newname: 设置挂载点的设备名称

第19-20行:返回结果

        }return mnt;
  • return mnt: 返回分配好的vfsmount指针
    • 成功: 返回有效的vfsmount指针
    • 失败: 返回NULL

停用和销毁一个超级块deactivate_super

void deactivate_super(struct super_block *s)
{struct file_system_type *fs = s->s_type;if (atomic_dec_and_lock(&s->s_active, &sb_lock)) {s->s_count -= S_BIAS-1;spin_unlock(&sb_lock);down_write(&s->s_umount);fs->kill_sb(s);put_filesystem(fs);put_super(s);}
}

函数功能

deactivate_super函数用于停用和销毁一个超级块(super_block),当文件系统不再需要时清理相关资源。它是文件系统卸载过程的核心函数

代码逐段解释

第1行:函数声明

void deactivate_super(struct super_block *s)
  • 返回类型: void - 无返回值
  • 参数: struct super_block *s - 指向要停用的超级块的指针
  • 功能: 停用超级块并释放相关资源

第2行:获取文件系统类型

        struct file_system_type *fs = s->s_type;
  • s->s_type: 超级块对应的文件系统类型(如ext4等)
  • fs: 保存文件系统类型指针,后续使用

第3行:原子递减和锁定检查

        if (atomic_dec_and_lock(&s->s_active, &sb_lock)) {

这是函数的核心条件检查:

atomic_dec_and_lock操作:

// 原子性地执行两个操作:
1. atomic_dec(&s->s_active): 将活动引用计数减1
2. 如果减1后计数为0,获取sb_lock自旋锁// 返回值:
// true: 计数减到0且成功获取锁
// false: 计数不为0,不获取锁
  • &s->s_active: 超级块的活动引用计数
  • &sb_lock: 超级块全局自旋锁,保护超级块状态

第4行:调整超级块计数

                s->s_count -= S_BIAS-1;
  • s->s_count: 超级块的另一个引用计数
  • S_BIAS: 一个偏置常量,用于引用计数管理
  • 操作: 从s_count中减去S_BIAS-1
  • 目的: 调整引用计数,为后续清理做准备

第5行:释放超级块锁

                spin_unlock(&sb_lock);
  • spin_unlock(&sb_lock): 释放之前获取的超级块全局锁

第6行:获取卸载写锁

                down_write(&s->s_umount);
  • down_write(&s->s_umount): 获取超级块的卸载读写信号量的写锁
  • 目的: 确保在卸载过程中没有其他线程可以挂载或访问该超级块
  • 阻塞可能: 如果其他线程持有读锁,会阻塞等待

第7行:调用文件系统的kill_sb方法

                fs->kill_sb(s);
  • fs->kill_sb: 文件系统类型中定义的"kill superblock"函数指针
  • 作用: 执行文件系统特定的清理操作
  • 不同文件系统的实现:
    • 释放文件系统特定资源
    • 同步脏数据到磁盘
    • 关闭底层设备

第8行:释放文件系统类型引用

                put_filesystem(fs);
  • put_filesystem(fs): 减少文件系统类型的引用计数
  • 作用: 如果引用计数为0,可能卸载文件系统模块
  • 对应操作: 与get_filesystem配对使用

第9行:释放超级块

                put_super(s);
  • put_super(s): 释放超级块结构本身
  • 内部操作:
    • 释放超级块占用的内存
    • 清理相关数据结构
    • 从全局超级块列表中移除

减少文件系统类型的引用计数put_filesystem

void put_filesystem(struct file_system_type *fs)
{module_put(fs->owner);
}
static inline void module_put(struct module *module)
{if (module) {unsigned int cpu = get_cpu();local_dec(&module->ref[cpu].count);/* Maybe they're waiting for us to drop reference? */if (unlikely(!module_is_live(module)))wake_up_process(module->waiter);put_cpu();}
}

函数功能

put_filesystem函数用于减少文件系统类型的引用计数,当引用计数降为0时可能触发文件系统模块的卸载。module_put是内核模块引用计数管理的核心函数

代码逐段解释

put_filesystem 函数

第1-3行:put_filesystem实现
void put_filesystem(struct file_system_type *fs)
{module_put(fs->owner);
}
  • 参数: struct file_system_type *fs - 文件系统类型指针
  • fs->owner: 指向拥有该文件系统的内核模块
  • module_put(fs->owner): 调用模块引用计数减少函数
  • 作用: 减少文件系统对应内核模块的引用计数

module_put 函数

第1行:函数声明
static inline void module_put(struct module *module)
  • static inline: 静态内联函数,减少函数调用开销
  • 参数: struct module *module - 内核模块指针
第2-3行:空指针检查
        if (module) {
  • 检查模块指针是否有效
  • 如果module为NULL,直接跳过所有操作
第4行:获取当前CPU编号
                unsigned int cpu = get_cpu();
  • get_cpu(): 获取当前CPU编号并禁用内核抢占
  • 作用:
    • 防止在操作过程中被调度到其他CPU
    • 确保引用计数操作的原子性和一致性
第5行:减少每CPU引用计数
                local_dec(&module->ref[cpu].count);
  • module->ref[cpu].count: 当前CPU对应的模块引用计数
  • local_dec(): 原子性地减少本地CPU的引用计数
  • 每CPU变量优势:
    • 避免CPU间的缓存行竞争
    • 提高并发性能
第6-8行:检查模块状态并唤醒等待者
                /* Maybe they're waiting for us to drop reference? */if (unlikely(!module_is_live(module)))wake_up_process(module->waiter);
  • unlikely(!module_is_live(module)):
    • module_is_live(module): 检查模块是否处于活动状态(非MODULE_STATE_GOING)
    • unlikely(): 提示编译器这个条件很少成立,优化分支预测
  • wake_up_process(module->waiter):
    • 如果模块不在活动状态,唤醒等待进程
    • module->waiter: 等待模块卸载完成的进程
第9行:恢复内核抢占
                put_cpu();}
  • put_cpu(): 恢复内核抢占状态
  • get_cpu()配对使用,恢复正常的调度行为

创建和管理块设备的伪文件系统超级块get_sb

static struct super_block *bd_get_sb(struct file_system_type *fs_type,int flags, const char *dev_name, void *data)
{return get_sb_pseudo(fs_type, "bdev:", &bdev_sops, 0x62646576);
}static struct file_system_type bd_type = {.name           = "bdev",.get_sb         = bd_get_sb,.kill_sb        = kill_anon_super,
};
struct super_block *
get_sb_pseudo(struct file_system_type *fs_type, char *name,struct super_operations *ops, unsigned long magic)
{struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL);static struct super_operations default_ops = {.statfs = simple_statfs};struct dentry *dentry;struct inode *root;struct qstr d_name = {.name = name, .len = strlen(name)};if (IS_ERR(s))return s;s->s_flags = MS_NOUSER;s->s_maxbytes = ~0ULL;s->s_blocksize = 1024;s->s_blocksize_bits = 10;s->s_magic = magic;s->s_op = ops ? ops : &default_ops;root = new_inode(s);if (!root)goto Enomem;root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR;root->i_uid = root->i_gid = 0;root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME;dentry = d_alloc(NULL, &d_name);if (!dentry) {iput(root);goto Enomem;}dentry->d_sb = s;dentry->d_parent = dentry;d_instantiate(dentry, root);s->s_root = dentry;s->s_flags |= MS_ACTIVE;return s;Enomem:up_write(&s->s_umount);deactivate_super(s);return ERR_PTR(-ENOMEM);
}

整体功能

这些函数用于创建和管理块设备的伪文件系统超级块,为内核中的块设备提供统一的文件系统接口

代码逐段解释

bd_get_sb 函数

第1-4行:bd_get_sb实现
static struct super_block *bd_get_sb(struct file_system_type *fs_type,int flags, const char *dev_name, void *data)
{return get_sb_pseudo(fs_type, "bdev:", &bdev_sops, 0x62646576);
}
  • 参数: 标准的文件系统挂载参数
  • get_sb_pseudo: 创建伪文件系统超级块
  • 参数说明:
    • fs_type: 文件系统类型
    • "bdev:": 伪文件系统名称前缀
    • &bdev_sops: 块设备超级块操作函数集
    • 0x62646576: 魔数,ASCII对应"bdev"

bd_type 文件系统类型定义

第6-10行:块设备文件系统类型
static struct file_system_type bd_type = {.name           = "bdev",.get_sb         = bd_get_sb,.kill_sb        = kill_anon_super,
};
  • .name = "bdev": 文件系统名称为"bdev"
  • .get_sb = bd_get_sb: 挂载时调用bd_get_sb创建超级块
  • .kill_sb = kill_anon_super: 卸载时调用匿名超级块清理函数

get_sb_pseudo 函数

第13-15行:函数声明和变量定义
struct super_block *
get_sb_pseudo(struct file_system_type *fs_type, char *name,struct super_operations *ops, unsigned long magic)
{struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL);static struct super_operations default_ops = {.statfs = simple_statfs};struct dentry *dentry;struct inode *root;struct qstr d_name = {.name = name, .len = strlen(name)};
  • 参数: 文件系统类型、名称、操作函数集、魔数
  • sget(): 获取或创建超级块
  • default_ops: 默认操作函数集,只包含statfs
  • d_name: 目录名称结构,初始化名称和长度
第17-18行:错误检查
        if (IS_ERR(s))return s;
  • IS_ERR(s): 检查超级块创建是否失败
  • 如果失败直接返回错误指针
第20-25行:超级块基本设置
        s->s_flags = MS_NOUSER;s->s_maxbytes = ~0ULL;s->s_blocksize = 1024;s->s_blocksize_bits = 10;s->s_magic = magic;s->s_op = ops ? ops : &default_ops;

详细设置:

  • MS_NOUSER: 标记为用户不可挂载的文件系统
  • s_maxbytes = ~0ULL: 最大文件大小为无符号长整型的最大值
  • s_blocksize = 1024: 块大小1KB
  • s_blocksize_bits = 10: 块大小位数(2^10=1024)
  • s_magic = magic: 设置文件系统魔数
  • s_op: 设置超级块操作函数集,如果ops为空使用默认
第26-32行:创建根inode
        root = new_inode(s);if (!root)goto Enomem;root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR;root->i_uid = root->i_gid = 0;root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME;
  • new_inode(s): 为超级块创建新的inode
  • 权限设置:
    • S_IFDIR: 目录类型
    • S_IRUSR | S_IWUSR: 用户读写权限
  • 所有权: uidgid都设为0(root)
  • 时间戳: 所有时间设为当前时间
第33-39行:创建根dentry
        dentry = d_alloc(NULL, &d_name);if (!dentry) {iput(root);goto Enomem;}dentry->d_sb = s;dentry->d_parent = dentry;d_instantiate(dentry, root);s->s_root = dentry;
  • d_alloc(NULL, &d_name): 分配dentry结构
  • 错误处理: 如果dentry分配失败,释放inode并跳转到错误处理
  • dentry设置:
    • d_sb = s: 指向所属超级块
    • d_parent = dentry: 父目录指向自己(根目录)
  • d_instantiate: 将dentryinode关联
  • s->s_root = dentry: 设置超级块的根dentry
第40-41行:激活超级块并返回
        s->s_flags |= MS_ACTIVE;return s;
  • MS_ACTIVE: 标记超级块为活跃状态
  • 返回创建好的超级块指针
第43-46行:错误处理
Enomem:up_write(&s->s_umount);deactivate_super(s);return ERR_PTR(-ENOMEM);
  • up_write(&s->s_umount): 释放超级块写锁
  • deactivate_super(s): 停用超级块
  • ERR_PTR(-ENOMEM): 返回内存不足错误

分配并初始化一个超级块alloc_super

static struct super_block *alloc_super(void)
{struct super_block *s = kmalloc(sizeof(struct super_block),  GFP_USER);static struct super_operations default_op;if (s) {memset(s, 0, sizeof(struct super_block));if (security_sb_alloc(s)) {kfree(s);s = NULL;goto out;}INIT_LIST_HEAD(&s->s_dirty);INIT_LIST_HEAD(&s->s_io);INIT_LIST_HEAD(&s->s_files);INIT_LIST_HEAD(&s->s_instances);INIT_HLIST_HEAD(&s->s_anon);init_rwsem(&s->s_umount);sema_init(&s->s_lock, 1);down_write(&s->s_umount);s->s_count = S_BIAS;atomic_set(&s->s_active, 1);sema_init(&s->s_vfs_rename_sem,1);sema_init(&s->s_dquot.dqio_sem, 1);sema_init(&s->s_dquot.dqonoff_sem, 1);init_rwsem(&s->s_dquot.dqptr_sem);init_waitqueue_head(&s->s_wait_unfrozen);s->s_maxbytes = MAX_NON_LFS;s->dq_op = sb_dquot_ops;s->s_qcop = sb_quotactl_ops;s->s_op = &default_op;}
out:return s;
}

函数功能

alloc_super函数用于分配并初始化一个超级块(super_block)结构体。它是文件系统挂载时创建超级块的核心函数,负责设置超级块的所有基本属性和同步机制

代码逐段解释

第1-3行:函数声明和内存分配

static struct super_block *alloc_super(void)
{struct super_block *s = kmalloc(sizeof(struct super_block),  GFP_USER);static struct super_operations default_op;
  • 返回类型: struct super_block * - 超级块指针
  • kmalloc(sizeof(struct super_block), GFP_USER):
    • 分配超级块结构体大小的内存
    • GFP_USER: 用户空间触发的分配标志
  • static struct super_operations default_op:
    • 静态默认超级块操作结构
    • 静态变量在首次使用时初始化为零

第4-10行:内存初始化和安全检查

        if (s) {memset(s, 0, sizeof(struct super_block));if (security_sb_alloc(s)) {kfree(s);s = NULL;goto out;}
  • if (s): 检查内存分配是否成功
  • memset(s, 0, sizeof(struct super_block)): 将整个超级块结构清零
  • security_sb_alloc(s): Linux安全模块(LSM)钩子函数
    • 进行安全相关的初始化
    • 如果安全检查失败,释放内存并跳转到out标签

第11-15行:初始化链表头

                INIT_LIST_HEAD(&s->s_dirty);INIT_LIST_HEAD(&s->s_io);INIT_LIST_HEAD(&s->s_files);INIT_LIST_HEAD(&s->s_instances);INIT_HLIST_HEAD(&s->s_anon);

各个链表的用途:

  • s_dirty: 脏inode链表
  • s_io: 需要写入磁盘的inode链表
  • s_files: 该文件系统打开的文件链表
  • s_instances: 同类型文件系统实例链表
  • s_anon: 匿名dentry的哈希链表

第16-19行:初始化同步原语

                init_rwsem(&s->s_umount);sema_init(&s->s_lock, 1);down_write(&s->s_umount);
  • init_rwsem(&s->s_umount): 初始化卸载读写信号量
  • sema_init(&s->s_lock, 1): 初始化超级块锁为1(可用状态)
  • down_write(&s->s_umount): 获取卸载写锁,防止并发卸载

第20-21行:设置引用计数

                s->s_count = S_BIAS;atomic_set(&s->s_active, 1);
  • s->s_count = S_BIAS: 设置超级块引用计数偏置值
    • S_BIAS是一个大数,防止过早释放
  • atomic_set(&s->s_active, 1): 原子性地设置活动引用计数为1

第22-26行:初始化更多同步机制

                sema_init(&s->s_vfs_rename_sem,1);sema_init(&s->s_dquot.dqio_sem, 1);sema_init(&s->s_dquot.dqonoff_sem, 1);init_rwsem(&s->s_dquot.dqptr_sem);init_waitqueue_head(&s->s_wait_unfrozen);

同步原语详解:

  • s_vfs_rename_sem: VFS重命名操作信号量
  • dqio_sem: 磁盘配额I/O信号量
  • dqonoff_sem: 配额启用/禁用信号量
  • dqptr_sem: 配额指针读写信号量
  • s_wait_unfrozen: 等待文件系统解冻的等待队列

第27-30行:设置操作函数和限制

                s->s_maxbytes = MAX_NON_LFS;s->dq_op = sb_dquot_ops;s->s_qcop = sb_quotactl_ops;s->s_op = &default_op;}
  • s->s_maxbytes = MAX_NON_LFS: 设置最大文件大小限制
    • MAX_NON_LFS通常为2GB,避免大文件支持问题
  • dq_op = sb_dquot_ops: 设置磁盘配额操作函数
  • s_qcop = sb_quotactl_ops: 设置配额控制操作函数
  • s_op = &default_op: 设置默认的超级块操作函数

第31-33行:返回结果

out:return s;
}
  • out:: 标签,用于错误处理跳转
  • return s: 返回超级块指针(成功或NULL)

获取或创建超级块sget

struct super_block *sget(struct file_system_type *type,int (*test)(struct super_block *,void *),int (*set)(struct super_block *,void *),void *data)
{struct super_block *s = NULL;struct list_head *p;int err;retry:spin_lock(&sb_lock);if (test) list_for_each(p, &type->fs_supers) {struct super_block *old;old = list_entry(p, struct super_block, s_instances);if (!test(old, data))continue;if (!grab_super(old))goto retry;if (s)destroy_super(s);return old;}if (!s) {spin_unlock(&sb_lock);s = alloc_super();if (!s)return ERR_PTR(-ENOMEM);goto retry;}err = set(s, data);if (err) {spin_unlock(&sb_lock);destroy_super(s);return ERR_PTR(err);}s->s_type = type;strlcpy(s->s_id, type->name, sizeof(s->s_id));list_add_tail(&s->s_list, &super_blocks);list_add(&s->s_instances, &type->fs_supers);spin_unlock(&sb_lock);get_filesystem(type);return s;
}

函数功能

sget函数用于获取或创建超级块,它是VFS层中超级块管理的核心函数。它实现了超级块的重用机制,避免相同文件系统的重复挂载,同时支持超级块的查找、创建和初始化

代码逐段解释

第1-6行:函数声明和变量定义

struct super_block *sget(struct file_system_type *type,int (*test)(struct super_block *,void *),int (*set)(struct super_block *,void *),void *data)
{struct super_block *s = NULL;struct list_head *p;int err;
  • 参数:
    • type: 文件系统类型指针
    • test: 测试函数指针,用于检查现有超级块是否匹配
    • set: 设置函数指针,用于初始化新超级块
    • data: 传递给test和set函数的用户数据
  • 局部变量:
    • s: 新超级块指针,初始为NULL
    • p: 链表遍历指针
    • err: 错误码

第7行:重试标签

retry:
  • 用于在获取超级块失败时重新尝试的标签

第8-19行:查找现有超级块

        spin_lock(&sb_lock);if (test) list_for_each(p, &type->fs_supers) {struct super_block *old;old = list_entry(p, struct super_block, s_instances);if (!test(old, data))continue;if (!grab_super(old))goto retry;if (s)destroy_super(s);return old;}
  • spin_lock(&sb_lock): 获取超级块全局自旋锁
  • if (test) list_for_each(p, &type->fs_supers):
    • 如果提供了test函数,遍历该文件系统类型的所有超级块实例
  • old = list_entry(p, struct super_block, s_instances):
    • 从链表节点获取超级块结构体指针
  • if (!test(old, data)) continue:
    • 调用test函数检查超级块是否匹配,不匹配则继续遍历
  • if (!grab_super(old)) goto retry:
    • 尝试获取现有超级块的引用,如果失败则重试
  • if (s) destroy_super(s):
    • 如果之前创建了新超级块s,现在销毁它
  • return old: 返回找到的现有超级块

第20-26行:创建新超级块

        if (!s) {spin_unlock(&sb_lock);s = alloc_super();if (!s)return ERR_PTR(-ENOMEM);goto retry;}
  • if (!s): 如果没有创建新超级块且没找到现有超级块
  • spin_unlock(&sb_lock): 释放锁,因为alloc_super可能睡眠
  • s = alloc_super(): 分配新超级块
  • if (!s) return ERR_PTR(-ENOMEM): 分配失败返回内存不足错误
  • goto retry: 跳转到retry,重新尝试查找(可能其他线程已创建)

第28-33行:初始化新超级块

        err = set(s, data);if (err) {spin_unlock(&sb_lock);destroy_super(s);return ERR_PTR(err);}
  • err = set(s, data): 调用set函数初始化新超级块
  • 错误处理: 如果set失败,释放锁,销毁超级块,返回错误

第34-39行:注册新超级块

        s->s_type = type;strlcpy(s->s_id, type->name, sizeof(s->s_id));list_add_tail(&s->s_list, &super_blocks);list_add(&s->s_instances, &type->fs_supers);spin_unlock(&sb_lock);
  • s->s_type = type: 设置超级块的文件系统类型
  • strlcpy(s->s_id, type->name, sizeof(s->s_id)): 复制文件系统名称到s_id
  • list_add_tail(&s->s_list, &super_blocks): 添加到全局超级块链表
  • list_add(&s->s_instances, &type->fs_supers): 添加到文件系统类型的实例链表
  • spin_unlock(&sb_lock): 释放超级块全局锁

第40-41行:增加引用并返回

        get_filesystem(type);return s;
  • get_filesystem(type): 增加文件系统类型的引用计数
  • return s: 返回新创建的超级块

设计模式分析

模板方法模式

// sget提供算法框架:
1. 查找现有实例
2. 创建新实例  
3. 初始化新实例// test和set函数由具体文件系统实现:
- 不同的匹配逻辑
- 不同的初始化方式

重试机制解决竞态条件

// 经典的Linux内核并发模式:
retry:spin_lock();// 检查条件if (条件失败) {spin_unlock();// 可能睡眠的操作goto retry;}// 执行操作spin_unlock();

性能优化

避免重复挂载

// 通过重用机制:
// 相同参数的挂载请求返回同一超级块
// 减少内存使用和初始化开销

锁粒度优化

// 精细的锁控制:
- 链表操作时持有锁
- 内存分配时释放锁
- 平衡并发性和安全性

安全地获取一个超级块的引用grab_super

static int grab_super(struct super_block *s)
{s->s_count++;spin_unlock(&sb_lock);down_write(&s->s_umount);if (s->s_root) {spin_lock(&sb_lock);if (s->s_count > S_BIAS) {atomic_inc(&s->s_active);s->s_count--;spin_unlock(&sb_lock);return 1;}spin_unlock(&sb_lock);}up_write(&s->s_umount);put_super(s);yield();return 0;
}

函数功能

grab_super函数用于安全地获取一个超级块的引用。它实现了超级块引用获取的并发安全机制,确保在获取引用时超级块不会被意外卸载

代码逐段解释

第1行:函数声明

static int grab_super(struct super_block *s)
  • static: 静态函数,只在当前文件内可见
  • 返回类型: int - 1表示成功,0表示失败
  • 参数: struct super_block *s - 要获取引用的超级块指针

第2-3行:增加引用计数并释放锁

        s->s_count++;spin_unlock(&sb_lock);
  • s->s_count++: 增加超级块的引用计数
  • spin_unlock(&sb_lock): 释放超级块全局自旋锁
  • 为什么立即释放锁?: 因为后续的down_write操作可能睡眠,不能在持有自旋锁时调用

第4行:获取卸载写锁

        down_write(&s->s_umount);
  • down_write(&s->s_umount): 获取超级块卸载读写信号量的写锁
  • 作用: 防止在引用获取过程中超级块被卸载
  • 可能阻塞: 如果其他线程正在卸载超级块,会等待直到卸载完成

第5行:检查根目录是否存在

        if (s->s_root) {
  • s->s_root: 检查超级块是否有有效的根目录
  • 意义: 如果根目录存在,说明超级块是完整初始化的,可以安全使用

第6-12行:成功获取引用的路径

                spin_lock(&sb_lock);if (s->s_count > S_BIAS) {atomic_inc(&s->s_active);s->s_count--;spin_unlock(&sb_lock);return 1;}spin_unlock(&sb_lock);
  1. spin_lock(&sb_lock): 重新获取超级块全局锁
  2. if (s->s_count > S_BIAS):
    • 检查引用计数是否大于偏置值
    • S_BIAS是一个大数,用于防止过早释放
    • 这个条件确保超级块没有被标记为待释放
  3. atomic_inc(&s->s_active): 原子性地增加活动引用计数
  4. s->s_count--: 减少之前增加的s_count(因为要用s_active
  5. spin_unlock(&sb_lock): 释放全局锁
  6. return 1: 返回成功

第13-17行:失败处理路径

        }up_write(&s->s_umount);put_super(s);yield();return 0;
  • up_write(&s->s_umount): 释放卸载写锁
  • put_super(s): 减少超级块引用计数,可能触发释放
  • yield(): 让出CPU,给其他线程运行机会
    • grab_super失败通常意味着另一个线程正在释放这个超级块
    • 如果没有yield(),释放超级块的线程可能还没有得到CPU时间,该线程就继续重试
  • return 0: 返回失败

详细机制分析

引用计数管理策略

超级块引用管理
s_count
s_active
临时引用
用于防止释放
活动引用
表示真正使用者
快速增减
不涉及复杂操作
原子操作
涉及状态检查
grab_super中临时增加
成功获取时转为活动引用

锁的使用顺序和目的

// 锁序列分析:
1. sb_lock (自旋锁): - 保护超级块链表和引用计数- 不能长时间持有(可能睡眠)2. s_umount (读写信号量):- 保护超级块卸载过程- 可以长时间持有,允许睡眠

设计原理总结

双重检查锁定模式

// grab_super使用的模式:
1. 快速检查: s_count++ (持有sb_lock)
2. 详细检查: down_write(s_umount) + 检查s_root
3. 最终确认: 重新检查s_count > S_BIAS (持有sb_lock)// 确保在所有关键阶段状态一致
http://www.dtcms.com/a/509019.html

相关文章:

  • 綦江建站哪家正规做服务的网站吗
  • 怎样看一个网站做的网络广告58企业网站怎么做
  • Nature Immunology | 人类皮肤成纤维细胞单细胞和空间转录组图谱揭示不同组织中与疾病相关的成纤维细胞亚型的共性
  • Redis Stream:高效消息队列的解析与应用
  • 网站开发技术历史天津网络项目公司
  • 西安做的好的网站公司关掉wordpress站点
  • qq刷赞网站咋做网站备案去哪
  • 营销型网站 案例深圳网站建设开发
  • 赣州城乡建设局网站做外贸网站咨询
  • C++项目实战1:多角色管理系统总结
  • Sibyl 框架里有没有 迭代过程
  • Linux系统编程:(三)基础指令详解(2)
  • 肇庆住房和城乡建设部网站中国数据网
  • VS创建C++动态库和C#访问过程
  • Linux服务器编程实践57-功能强大的网络信息函数getaddrinfo:支持IPv4与IPv6
  • 美食网站html静态mooc网站建设
  • 网站备案申请书最新网站建设语言
  • DAX的日期相减DATEDIFF 函数
  • day15(do-while循环语句)
  • 并行任务太多,优先级怎么排
  • 自建商城网站用什么技术好公司网站建设款计什么科目
  • wordpress建站解析做网站怎么把导航每个页面都有
  • 动态内容可以缓存到CDN吗?
  • 东莞乐从网站建设邯郸专业网站建设报价
  • 服务好的企业建站alexa全球排名
  • 郑州服装网站建设公司阿里 域名解析 网站建设
  • 【学习系列】SAP RAP 11:行为定义-Feature Control
  • 国社科申报选题秘籍:如何把“具体问题”变成评审眼中的“好问题”?利用AI只需三步高效辅助(附AI提示词模板)
  • 张店网站优化网站登陆页面怎么做
  • 如何建设公司的网站上海响应式建站