Linux内核有什么之块设备驱动有什么第七回 —— 邂逅的三个文件系统之二:实际文件系统(4)

news2025/1/23 13:15:04

接前一篇文章:Linux内核有什么之块设备驱动有什么第六回 —— 邂逅的三个文件系统之二:实际文件系统(3)

本文内容参考:

《Linux设备驱动开发详解 —— 基于最新的Linux4.0内核》 宋宝华,机械工业出版社

34 | 块设备(上):如何建立代理商销售模式?-趣谈Linux操作系统-极客时间

特此致谢!

上回书以F2FS文件系统为例,讲解了块设备邂逅的第二个文件系统 —— 实际文件系统的挂载流程中的mount_bdev函数调用的第1个函数lookup_bdev。再来看一下调用流程:

struct file_system_type f2fs_fs_type --->

    f2fs_fs_type->mount ---> 

        f2fs_mount --->

            mount_bdev --->

                lookup_bdev

为了便于理解和回顾,再次贴出mount_bdev函数的源码,在Linux内核源码根目录/fs/super.c中,代码如下:

struct dentry *mount_bdev(struct file_system_type *fs_type,
	int flags, const char *dev_name, void *data,
	int (*fill_super)(struct super_block *, void *, int))
{
	struct super_block *s;
	int error;
	dev_t dev;
 
	error = lookup_bdev(dev_name, &dev);
	if (error)
		return ERR_PTR(error);
 
	flags |= SB_NOSEC;
	s = sget(fs_type, test_bdev_super, set_bdev_super, flags, &dev);
	if (IS_ERR(s))
		return ERR_CAST(s);
 
	if (s->s_root) {
		if ((flags ^ s->s_flags) & SB_RDONLY) {
			deactivate_locked_super(s);
			return ERR_PTR(-EBUSY);
		}
	} else {
		/*
		 * We drop s_umount here because we need to open the bdev and
		 * bdev->open_mutex ranks above s_umount (blkdev_put() ->
		 * bdev_mark_dead()). It is safe because we have active sb
		 * reference and SB_BORN is not set yet.
		 */
		super_unlock_excl(s);
		error = setup_bdev_super(s, flags, NULL);
		__super_lock_excl(s);
		if (!error)
			error = fill_super(s, data, flags & SB_SILENT ? 1 : 0);
		if (error) {
			deactivate_locked_super(s);
			return ERR_PTR(error);
		}
 
		s->s_flags |= SB_ACTIVE;
	}
 
	return dget(s->s_root);
}
EXPORT_SYMBOL(mount_bdev);

接下来来看mount_bdev()调用的第2个函数sget。代码片段如下:

	flags |= SB_NOSEC;
	s = sget(fs_type, test_bdev_super, set_bdev_super, flags, &dev);
	if (IS_ERR(s))
		return ERR_CAST(s);

这里注意老版本和新版本的区别,老版本这一部分代码如下:

这两者最大的区别就是函数的最后一个参数。老版本中是前一步得到的struct block_device *bdev;而新版本中则是dev_t *bdev(bdev实际上就是一个u32类型的变量,中间类型为__kernel_dev_t)。

sget函数也在Linux内核源码根目录/fs/super.c中,代码如下:

/**
 *	sget	-	find or create a superblock
 *	@type:	  filesystem type superblock should belong to
 *	@test:	  comparison callback
 *	@set:	  setup callback
 *	@flags:	  mount flags
 *	@data:	  argument to each of them
 */
struct super_block *sget(struct file_system_type *type,
			int (*test)(struct super_block *,void *),
			int (*set)(struct super_block *,void *),
			int flags,
			void *data)
{
	struct user_namespace *user_ns = current_user_ns();
	struct super_block *s = NULL;
	struct super_block *old;
	int err;

	/* We don't yet pass the user namespace of the parent
	 * mount through to here so always use &init_user_ns
	 * until that changes.
	 */
	if (flags & SB_SUBMOUNT)
		user_ns = &init_user_ns;

retry:
	spin_lock(&sb_lock);
	if (test) {
		hlist_for_each_entry(old, &type->fs_supers, s_instances) {
			if (!test(old, data))
				continue;
			if (user_ns != old->s_user_ns) {
				spin_unlock(&sb_lock);
				destroy_unused_super(s);
				return ERR_PTR(-EBUSY);
			}
			if (!grab_super_dead(old))
				goto retry;
			destroy_unused_super(s);
			return old;
		}
	}
	if (!s) {
		spin_unlock(&sb_lock);
		s = alloc_super(type, (flags & ~SB_SUBMOUNT), user_ns);
		if (!s)
			return ERR_PTR(-ENOMEM);
		goto retry;
	}

	err = set(s, data);
	if (err) {
		spin_unlock(&sb_lock);
		destroy_unused_super(s);
		return ERR_PTR(err);
	}
	s->s_type = type;
	strscpy(s->s_id, type->name, sizeof(s->s_id));
	list_add_tail(&s->s_list, &super_blocks);
	hlist_add_head(&s->s_instances, &type->fs_supers);
	spin_unlock(&sb_lock);
	get_filesystem(type);
	shrinker_register(s->s_shrink);
	return s;
}
EXPORT_SYMBOL(sget);

sget这个函数也是新老内核版本不一样了。老版本代码是这样:

static int set_bdev_super(struct super_block *s, void *data)
{
    s->s_bdev = data;
    s->s_dev = s->s_bdev->bd_dev;
    s->s_bdi = bdi_get(s->s_bdev->bd_bdi);
    return 0;
}

/**
  * sget - find or create a superblock
  * @type: filesystem type superblock should belong to
  * @test: comparison callback
  * @set: setup callback
  * @flags: mount flags
  * @data: argument to each of them
  */
struct super_block *sget(struct file_system_type *type, int (*test)(struct super_block *,void *), int (*set)(struct super_block *,void *), int flags, void *data)
{
    ......
    return sget_userns(type, test, set, flags, user_ns, data);
}

/**
  * sget_userns - find or create a superblock
  * @type: filesystem type superblock should belong to
  * @test: comparison callback
  * @set: setup callback
  * @flags: mount flags
  * @user_ns: User namespace for the super_block
  * @data: argument to each of them
  */
struct super_block *sget_userns(struct file_system_type *type, int (*test)(struct super_block *,void *), int (*set)(struct super_block *,void *), int flags, struct user_namespace *user_ns, void *data)
{
    struct super_block *s = NULL;
    struct super_block *old;
    int err;
    ......
    if (!s)
    {
        s = alloc_super(type, (flags & ~MS_SUBMOUNT), user_ns);
        ......
    }
    err = set(s, data);
    ......
    s->s_type = type;
    strlcpy(s->s_id, type->name, sizeof(s->s_id)); list_add_tail(&s->s_list, &super_blocks);
    hlist_add_head(&s->s_instances, &type->fs_supers);
    spin_unlock(&sb_lock);
    get_filesystem(type);
    register_shrinker(&s->s_shrink);
    return s;
}

通过新老版本内核代码可以看到,sget这个函数确实有比较大的改动了。

老版本中的机制是:将 block_device塞进superblock里面(在set_bdev_super函数中)。而sget要做的,就是分配一个 super_block,然后调用set_bdev_super这个回调函数。

新版本中的机制和老版本差不多,大体也是这个流程:

    if (!s)
    {
        s = alloc_super(type, (flags & ~MS_SUBMOUNT), user_ns);
        ......
    }
    err = set(s, data);

只不过新版本直接放在了sget函数中,而不像老版本中还多了一层函数调用(sget_userns)。

这里实际看一下新版本内核代码中的set_bdev_super函数的源码,在同文件(Linux内核源码根目录/fs/super.c)中,如下:

static int set_bdev_super(struct super_block *s, void *data)
{
	s->s_dev = *(dev_t *)data;
	return 0;
}

对比一下老版本的代码:

static int set_bdev_super(struct super_block *s, void *data)
{
    s->s_bdev = data;
    s->s_dev = s->s_bdev->bd_dev;
    s->s_bdi = bdi_get(s->s_bdev->bd_bdi);
    return 0;
}

可见,新版本内核代码在这一块更为精简了。

这里也给出test_bdev_super函数的代码,也在同文件中,如下:

static int test_bdev_super(struct super_block *s, void *data)
{
	return !(s->s_iflags & SB_I_RETIRED) && s->s_dev == *(dev_t *)data;
}

到此为止,mount 中一个块设备的过程就结束了。设备打开了,查找得到了dev_t dev,并且塞到了super_block结构(实例)中。

至此,mount_bdev函数就解析完了。欲知后事如何,且看下回分解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1978102.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

奥运会大规模使用中国AI大模型!

B站:啥都会一点的研究生公众号:啥都会一点的研究生 AI圈最近又发生了啥新鲜事? 巴黎奥运会大规模使用中国 AI 大模型 巴黎奥运会成为一场科技与体育的盛宴,其中包括了大量中国科技的应用。AI 技术将在多个方面发挥作用&#xf…

《计算机网络》(第8版)第7章 网络安全 复习笔记

第 7 章 网络安全 一、网络安全问题概述 1 计算机网络面临的安全性威胁 计算机网络上的通信面临两大类威胁,即被动攻击和主动攻击。 (1)被动攻击 这是指攻击者从网络上窃听他人的通信内容,通常把这类攻击称为截获。 &#xff08…

2.外部中断(EXTI)

理论 NVIC:嵌套向量中断控制器(解释教程) 外部通用中断线(EXTI0~EXTI15):每个GPIO设置成中断模式,与中断控制器连接的线 外部中断触发方式 上升沿触发、下降沿触发、双边沿触发 外部中断触发函数 在stm32f1xx_it.c文件…

【AI作图:奥运会游泳冠军】

画一个:水花,上半身,游泳冠军,泳池背景,面部明亮,眼神光,亚洲运动员,超高品质,真人,完美容颜,健美,健身,身材娇好&#xf…

Jackson常用注解详解

Hi 👋, Im shy 有人见尘埃,有人见星辰 Jackson常用注解详解 文章目录 Jackson常用注解详解0. 引入依赖1. JsonProperty2. JsonIgnore3. JsonFormat4. JsonInclude5. JsonCreator6. JsonValue7. JsonIgnoreProperties结论 Jackson是Java生态系统中广泛…

例题:使用一条命令将xxx目录下除了xx子目录之外的文件全部删除(find、管道、grep、exec)

文章目录 例题:删除xx子目录以外的所有目录和文件需求方式一方式二 例题:删除xx子目录以外的所有目录和文件 需求 使用一条命令将/opt目录下除了rh子目录之外的文件全部删除 [rootlocalhost opt]# cp /var/log/vm* /opt/ [rootlocalhost opt]# mkdir …

(一)循环依赖,你真的懂了吗?万字解析循环依赖底层原理 - 什么是Bean循环依赖?Spring是如何解决的?二级缓存可以解决吗?遇到相关报错如何处理?

theme: vuepress 一、相关知识点简介 Spring Boot是基于Spring框架的一个快速开发平台,旨在简化Spring应用的创建和部署。通过提供一系列开箱即用的默认配置和自动化工具,Spring Boot使开发者能够专注于业务逻辑,而无需处理复杂的配置和依赖…

【Python机器学习】支持向量机——在复杂数据上应用核函数

上图中,数据中存在某种可以识别的模式,其中一个问题就是:我们能否想线性情况一样,利用强大的工具来捕捉数据中的这种模式? 利用核函数将数据映射到高维空间 在上图中,数据点处于一个圆中,人类…

《零散知识点 · 自定义 HandleMapping》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…

[独家原创] CPO-RBF多特征分类预测 优化宽度+中心值+连接权值 (多输入单输出)Matlab代码

[独家原创] CPO-RBF多特征分类预测 优化宽度中心值连接权值 (多输入单输出)Matlab代码 目录 [独家原创] CPO-RBF多特征分类预测 优化宽度中心值连接权值 (多输入单输出)Matlab代码效果一览基本介绍程序设计参考资料 效果一览 基本…

算法--初阶

1、tips 1.1、set求交集 {1,2,3} & {2,3} & {1,2} {2} 其实就是位运算, 只有set可以这样使用, list没有这种用法 {1,2,3} | {2,3, 4} | {1,2} {1, 2, 3, 4} 并集 1.2、*与** * 序列(列表、元组)解包,如果是字典,那…

15.75.【C语言】表达式求值

目录 一.整型提升 1.定义 2. 一.整型提升 1.定义 C语言中整型算术运算总是至少以缺省(默认)整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升 2.整型提…

C++初学者指南-5.标准库(第二部分)--移除元素算法

C初学者指南-5.标准库(第二部分)–移除元素算法 文章目录 C初学者指南-5.标准库(第二部分)--移除元素算法remove / remove_ifremove_copy / remove_copy_ifunique / unique_copyerase / erase_if相关内容 不熟悉 C 的标准库算法? ⇒ 简介 remove / remove…

还有谁分不清Oracle认证里的OCA、OCP、OCM?

在IT行业,Oracle认证是许多专业人士提升技能和职业竞争力的重要途径。 Oracle认证是一套由Oracle公司提供的全球认可的专业资格认证体系,旨在证明个人在Oracle数据库及相关技术领域的专业技能和知识水平。 Oracle认证作为数据库认证中的天花板&#xff0…

目标检测——GDXray数据集转为YOLO格式

关于该数据集的介绍可以看我写的另一篇博客:链接 论文题目:《GDXray: The Database of X-ray Images for Nondestructive Testing》论文链接:https://link.springer.com/article/10.1007/s10921-015-0315-7 Github链接: https:…

JavaScript小本本|JavaScript 对象方法定义的演变

在微信中阅读,欢迎关注公众号:CodeFit。 创作不易,如果你觉得这篇文章对您有帮助,请不要忘了 点赞、分享 和 关注,为我的 持续创作 提供 动力! 欢迎订阅《Vue 3.x 必修课|2024》:htt…

JAVA项目基于SpringBoot的外卖点餐管理系统

目录 一、前言 二、技术介绍 三、系统实现 四、论文参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 随着生活节…

C++20中的模块

大多数C项目使用多个翻译单元(translation units),因此它们需要在这些单元之间共享声明和定义(share declarations and definitions)。headers的使用在这方面非常突出。模块(module)是一种language feature,用于在翻译单元之间共享声明和定义。它们是某些…

追问试面试系列:Dubbo

欢迎来到Dubbo系列,在面试中被问到Dubbo相关的问题时,大部分都是简历上写了Dubbo,或者面试官想尝试问问你对Dubbo是否了解。 本系列主要是针对面试官通过一个点就使劲儿往下问的情况。 面试官:说说你们项目亮点 好的面试官 我们这个项目的技术亮点在于采用了Spring Cloud…

正点原子imx6ull-mini-Linux驱动之Linux I2C 驱动实验(21)

I2C 是很常用的一个串行通信接口,用于连接各种外设、传感器等器件,在裸机篇已经对 I.MX6U 的 I2C 接口做了详细的讲解。本章我们来学习一下如何在 Linux 下开发 I2C 接口器件 驱动,重点是学习 Linux 下的 I2C 驱动框架,按照指定的…