Linux 式套娃,把“文件系统”安装在一个“文件”上?

背景

“文件”在文件系统之中,这是人人理解的概念。但“文件”之上还有一个文件系统?那岂不是成套娃了。但这个其实是可以的。这个就涉及到今天我们要讲的 loop 设备。

很多童鞋在学习 Linux 的文件系统时,涉及到对磁盘设备的格式化,挂载等操作,但苦于没有一个真实的硬盘,一时不知道如何实践。这种时候就可以使用一个文件来模拟块设备。这就是 loop 设备的作用。我们借助 loop 设备,可以让一个文件被当做一个块设备来访问。

举个例子,我们在 ext4 的文件系统目录下创建了一个 minix_test.img 文件,把它当作一个块设备,在上面格式化 minix 的文件系统,并挂载到 /mnt/minix 上。示意图如下:

图片

这种方式有两个很明显好处:

不需要真实的硬盘,就可以格式化、挂载、测试文件系统。

可以近距离的观察文件系统对块设备的使用,比如如何划分 inode 区域、数据区域、位图区域等。这些都将反馈到文件上。

如何使用 loop 设备?

接下来看下如何使用 loop 设备。我们将会用一个普通文件上格式化成 minix 文件系统,然后挂载到 Linux 目录树上。我们使用 loop 设备有两种方式:

  1. 一种是直接 mount 带上 -o loop 的参数。这种省去了显式创建 loop 设备的过程,步骤简单。
  2. 另一种方式是先显式的创建 loop 设备,该 loop 设备绑定一个文件,并提供了块设备的对外接口。我们就可以把这个 loop 设备当作一个普通的块设备文件,进行格式化,然后挂载到目录上。
方式一:mount -o loop

用 mount 挂载文件系统的时候,指定某个文件以 loop 设备的方式进行挂载。具体操作如下:

首先,我们创建一个 1GiB 的文件:

dd if=/dev/zero of=./minix_test.img bs=1M count=1024

然后,我们在这个文件上进行 minix 文件系统的格式化:

mkfs.minix ./minix_test.img

最后,我们用 loop 设备的方式进行挂载:

mount -o loop ./minix_test.img /mnt/minix/

这样挂载成功之后,就可以在 /mnt/minix 下进行操作了。该目录挂载的是 minix 类型的文件系统。minix 文件系统是一个磁盘类型的文件系统,它的数据是会写到磁盘进行持久化。所以,我们在这个 /mnt/minix 目录下做的任何操作,这些都会反映到 ./minix_test.img 这个文件上。这个文件就像磁盘一样,在这个上面承载了一个文件系统的数据。

可以尝试在 /mnt/minix 目录下创建一个文件,然后用 hexdump 工具查看 minix_test.img 的内容变化。如下:

# 在 minix 文件系统之上创建一个文件,并写入一个字符串
echo "hello world" >> /mnt/minix/hello.txt# 查看 minix_test.img 的内容
hexdump -C ./minix_test.img

如果你对 minix 文件系统的分区熟悉的话,就可以明显看到 minix 是如何在 minix_test.img 文件上划分的 inode 区域、数据区域、位图区域等。

方式二:先创建 loop 设备,再挂载使用

这种方式稍微步骤多一点,但其实更容易让人理解其中原理。实际的效果和方式一是等价的。我们可以使用 losetup 命令来管理 loop 设备。

首先,我们创建一个 1GiB 的文件:

dd if=/dev/zero of=./minix_test.img bs=1M count=1024

然后,我们在这个文件上进行 minix 文件系统的格式化:

mkfs.minix ./minix_test.img

再然后,创建和使用 loop 设备:

# 方式一:假定 /dev/loop5 是空闲可用的 loop 设备,下面把 /dev/loop5 和 minix_test.img 关联起来
losetup /dev/loop5 ./minix_test.img# 方式二:可以简单一点,让 losetup 命令自动找到一个空闲的 loop 设备,然后进行关联
losetup --find --show ./minix_test.img

创建完 loop 设备之后,可以用 losetup 命令列举当前所有的 loop 设备,和它们关联的文件。

最后,把 loop 设备挂载到目录上:

# 假设上一个步骤创建的是 /dev/loop5 
mount /dev/loop5 /mnt/minix

这种方式挂载的文件系统和方式一本质上是一样的。

mount 和 losetup 等命令的源代码都在 util-linux 开源库中,感兴趣的童鞋们可以自行查看。

什么是 loop 设备?

本质上来讲,loop 设备是块设备的一种特殊的驱动实现。接下来我们来简单看下 loop 设备的基本原理。

loop 设备的原理

loop 就是一种特殊的块设备驱动。loop 设备是一种 Linux 虚拟的伪设备,它和真实的块设备不同,它并不代表一种特定的硬件设备,而仅仅是满足 Linux 块设备接口的一个虚拟设备。它的作用就是把一个文件模拟成一个块设备。

loop 设备它是怎么模拟的块设备?

loop 设备的代码位于 Linux 的 drivers/block/loop.c 中。在这个文件中,它定义了块设备驱动的接口。

块设备驱动的编程范式:

  1. 首先,要分配并初始化一个 gendisk 结构体,这是内核代表块设备的核心结构体。它包含了与磁盘相关的信息,loop 设备作为一种特殊的块设备驱动,这个自然是不能少的。
  2. 然后,初始化一个请求队列。块设备使用请求队列来管理对设备的 I/O 请求。文件系统调用 submit_bio 的调用时,最终就是把请求投递到驱动的队列中。
  3. 然后,请求处理函数。这个很容易理解,队列里的请求总是要处理的,每个块设备驱动都可以自定义处理方式。
  4. 最后,块设备操作表(block_device_operations),这个将包含对设备的操作方法,比如打开,读写控制等。

loop 设备如何关联到后端“文件” ?

用户态的处理( losetup 或 mount )
  • 打开后端“文件”,拿到文件描述符。
  • 打开 loop 设备文件,拿到 loop 设备的描述符。
  • 调用 ioctl 把这两个关联起来ioctl(dev_fd, LOOP_CONFIGURE, &lc->config)
内核的处理
  • ioctl 的系统调用对应调用 loop 中的 lo_ioctl 函数。

对应了 block_device_operations 的 ioctl 方法。

  • 当设置参数为 LOOP_CONFIGURE 的时候,会调用 loop_configure 来分配和初始化 loop 设备。
  • loop_configure
  • 获取到后端“文件”的句柄,也就是 struct file* 结构。获取到之后,会做一些校验工作。然后初始化 loop 设备相关的结构体,队列等。

  • 最关键的当然还是把 loop 设备和后端“文件”的句柄关联起来:lo->lo_backing_file = file; 这样的话,等到读写 loop 设备的时候,就可以把请求转发过去。

loop 设备的请求来自于哪里?

loop 设备它对外就是一个块设备,如果在这个之上创建了文件系统,并被当作块设备挂载到目录上之后,那么它的请求来自于它之上的文件系统。

文件系统调用 submit_bio ,把请求投递到块层的队列中。每一个块层的设备它都需要实现一个入队的处理,以供 submit_bio 的流程中调用。

static const struct blk_mq_ops loop_mq_ops = {.queue_rq       = loop_queue_rq,.complete = lo_complete_rq,
};

loop 设备实现的入队方法就是 loop_queue_rq 。文件系统调用 submit_bio 之后最终就会调用到 loop_queue_rq 这个函数。

> loop_queue_rq> loop_queue_work

loop_queue_work 函数会把请求放到一个 lo->workqueue 队列中,每一个 loop 设备都对应有这么一个队列。在创建设备文件的时候会同步生成。

loop 设备如何把请求传递给后端文件?

loop 设备还有一个名为 loop_workfn 的函数,是专门用来处理投递到该设备的请求。

> loop_workfn> loop_handle_cmd> do_req_filebacked

在 do_req_filebacked 函数中,会按照不同的命令类型来处理请求,比如,写请求会调用 lo_write_simple ,读请求会调用 lo_read_simple 等。

在这个 lo_write_simple  函数中,就会调用 lo_write_bvec ,把写请求写入关联的“文件”中。

static int lo_write_simple(struct loop_device *lo, struct request *rq,loff_t pos)
{// lo_backing_file 代表当前 loop 设备关联的文件ret = lo_write_bvec(lo->lo_backing_file, &bvec, &pos);
}

在 lo_write_bvec 中,调用的是 vfs_iter_write 函数来进行写入。这个函数其实是 VFS 层的一个封装函数,所以相当于就是从顶层调用后端文件的写操作。

在 lo_write_bvec 中,最关键的是 lo->lo_backing_file 这个文件句柄的获取。它的类型是 struct file* ,代表了一个内核打开的文件。即该 loop 设备关联的文件。它的赋值就是在 loop 设备创建的时候。

Linux 套娃之后,I/O 链路是什么样的?

现在来看下,当我们使用 loop 设备,来挂载一个 minix 文件系统,它的 I/O 路径又会是怎么样的呢?

假定创建的 minix_test.img 位于一个 ext4 文件系统。

应用程序 -> 系统调用 -> vfs -> minix 文件系统 -> 块层 -> loop 设备 -> 绑定的文件 ->  vfs -> ext4 -> ...

示意图如下:

图片

loop 设备的典型应用有哪些?

其实,loop 设备在很多场景下我们都用过。以下是比较典型的例子:

  • 系统模拟和测试:可以使用 loop 设备来模拟不同的存储配置,无需使用物理硬件,就可以进行软件测试或系统配置实验。
  • 文件系统开发:开发者可以使用 loop 设备来挂载文件系统,从而方便地测试和调试新的文件系统。
  • ISO 映像挂载:Loop 设备还常用于挂载 ISO 文件,无需刻录到物理介质上,使其内容可直接访问。
  • 加密磁盘:loop 设备还能和一些加密技术(如dm-crypt)结合,因为 loop 设备可以绑定几乎任意类型的文件,这就给了人们无限的想象空间。我们可以创建一个加密的磁盘镜像,增强数据安全。

其实,Linux 的套娃还远不止这些。举个例子,loop 设备绑定的文件,它可能也是抽象出来的一个文件,比如是一个网络文件系统抽象出来的文件。那这条 I/O 链路就更长了。只要你敢想,在 Linux 中,存在无限套娃的可能。

总结

  • 没有磁盘也想玩磁盘类文件系统?可以,用 loop 设备。只要你有文件,Linux 的 loop 设备都可以把文件变成一个“块设备”。
  • loop 设备就是一种特殊的块设备驱动。
  • 文件之上的的文件系统的增、删、改、查的 I/O 请求,它都将落到文件上。这个可以让我们近距离的观察到文件系统是如何管理磁盘的。
  • hexdump 是个好工具,可以用来查看文件上的二进制内容。

来源:奇伢云存储 

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

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

相关文章

一个轻量级的TTS模型实现

1.环境 python 版本 3.9 2.训练数据集 本次采用LJSpeech数据集,百度网盘下载地址 链接:https://pan.baidu.com/s/1DDFmPpHQrTR_NvjAfwX-QA 提取码:1234 3.安装依赖 pip install TTS 4.工程结构 5代码部分 decoder.py import torch f…

注解详解系列 - @Repository:数据访问层组件

注解简介 在今天的注解详解系列中,我们将探讨Repository注解。Repository是Spring框架中的一个专用注解,用于标记数据访问层(DAO)组件。使用该注解可以让Spring自动检测和管理这些数据访问组件,并提供一致的异常处理机…

VueUse:Vue 3 开发中的强大 Hooks 库

一、什么是 VueUse? 二、安装和使用 VueUse 三、常用 Hooks 介绍 1. useDebounce 和 useThrottle useDebounce useThrottle 2. useRequest 3. useTitle 4. useCounter 5. useToggle 6. useFullscreen 7. useInterval 和 useTimeout useInterval useTim…

汇编基础之使用vscode写hello world

汇编语言(Assembly Language) 概述 汇编语言(Assembly Language)是一种低级编程语言,它直接对应于计算机的机器代码(machine code),但使用了更易读的文本符号。每台个人计算机都有…

MQTT协议有哪些优点和缺点

MQTT协议的优点和缺点可以归纳如下: 1.优点: ① 轻量级:MQTT是一种轻量级的二进制协议,相比于其他协议(如HTTP),它的数据包开销较小。这意味着在网络传输过程中,MQTT协议所产生的流…

iOS 18 Siri 升级之后都有哪些改变?

新界面 首先最显著的改变就是 Siri 的界面不同了,之前的界面是在打开 Siri 之后会出现一个圆形图案,而在 Siri 升级之后变成了屏幕边缘发出亮光。 来源:Apple 可在任意位置使用 苹果的生成式人工智能 Apple Intelligence 将为 Siri 提供支…

【区分vue2和vue3下的element UI Avatar 头像组件,分别详细介绍属性,事件,方法如何使用,并举例】

在 Vue 2 的 Element UI 和 Vue 3 的 Element Plus 中,Avatar 头像组件可能并没有直接作为官方组件库的一部分。然而,为了回答你的问题,我将假设 Element UI 和 Element Plus 在未来的版本中可能添加了 Avatar 组件,或者我们将使用…

redhat 7.1 打开python文件中文乱码

解释: 在Red Hat Enterprise Linux 7.1中打开Python文件时出现文字乱码通常是因为文件的编码格式与Python解释器默认的编码格式不一致所导致。Python 2.x 默认使用ASCII编码,而ASCII编码不支持中文等一些字符,因此在处理中文或其他非ASCII字…

注意力机制和Transformer模型各部分功能解释

文章目录 Transformer1、各部分功能解释2、通过例子解释a.输入预处理位置编码b.Encoder 的处理c.Decoder的输入Decoder的工作流程d.输出预测总结 Attention代码和原理理解 Transformer 运行机理: (1)假设我们需要进行文本生成任务。我们将已…

Spring Boot 项目中的 GC Root

可达性分析是一种用于判定对象是否可以被垃圾回收的算法。基本思想是通过一个称为“根集合”(GC Root)的起点集合,来追踪对象引用。如果一个对象从根集合开始可以被追踪到,那么它就是“可达”的,否则就是“不可达”的&…

Springboot集成SSE消息推送

SSE介绍 SSE(Server-Sent Events)的全称是服务器推送事件,它是一种基于 HTTP 协议的实时通信技术,用于在客户端和服务器之间建立持久、单向的链接,允许服务器向客户端发送异步消息。 了解 websocket 的小伙伴&…

SAP BC 换了logo后,其他人的logo都已经换了,但是其中有一台就是PRD 显示DEV的logo,从smw0上下载的是PRD

昨天终于发现是缓存的问题 GUI登录后 选项-本地数据-缓存 删除本地缓存文件,问题解决了

机器学习课程复习——聚类算法

Q:什么是硬聚类,什么是软聚类? 如果一个样本只能属于一个类,则称为硬聚类(hard clustering); 如果一个样本可以属于多个类,则称为软聚类(soft clustering)。 Q:聚类和分类的区别? 聚类分类学习类型无监督学习方法 不需要事先标记的数据 通过发现数据中的模式或结构来组…

sprintboot依赖管理和自动配置

springboot依赖管理和自动配置 依赖管理和自动配置依赖管理什么是依赖管理修改自动仲裁/默认版本号 starter场景启动器starter场景启动器基本介绍官方提供的starter第三方starter 自动配置自动配置基本介绍SpringBoot自动配置了哪些?如何修改默认配置如何修改默认扫描包结构re…

深入解析 iOS 应用启动过程:main() 函数前的四大步骤

深入解析 iOS 应用启动过程:main() 函数前的四大步骤 背景描述:使用 Objective-C 开发的 iOS 或者 MacOS 应用 在开发 iOS 应用时,我们通常会关注 main() 函数及其之后的执行逻辑,但在 main() 函数之前,系统已经为我们…

创建最基本的web服务器-http模块

在Node.js中,可以使用内置的http模块来创建一个最基本的web服务器。以下是一个简单的示例,它创建了一个HTTP服务器,该服务器监听一个端口,并在接收到请求时发送一个“Hello, World!”的响应。 // 引入http模块 const http requi…

leetcode 二分查找·系统掌握 寻找比目标字母大的最小字母

题目: 给你一个字符数组 letters,该数组按非递减顺序排序,以及一个字符 target。letters 里至少有两个不同的字符。 返回 letters 中大于 target 的最小的字符。如果不存在这样的字符,则返回 letters 的第一个字符。 题解&…

C++及cmake语法介绍

c/cmake学习 文章目录 c/cmake学习1. c1.1 基本模型1.1.1 for循环1.1.2 main函数1.1.2 带参数函数编译函数 2. CMAKE2.1 相关命令2.1.1 编译基本命令2.1.2 动态库静态库编译2.1.3 消息输出2.1.4 cmake变量常见参数1. 设置构建类型2. 设置编译器标志3. 指定编译器4. 设置安装路径…

机器学习_PCA

目录 一、概念 二、原理 三、步骤 四、实战 1、数据处理——转rgb为灰度图像 2、手动实现pca降维 3、查看信息保留数量 4、调用第三方库实现pca降维 五、小结 引入: 当说黄河五路和渤海三路交叉口的时候,这些路就类似于我们说的坐标系。而城市中的…

高等数学笔记(三):导数

一、导数概念 1.1 导数的定义 1.1.1 函数在一点处的导数与导函数 1.1.2 单侧导数 1.2 导数的几何意义 1.3 函数可导性与连续性的关系 二、函数的求导法则 2.1 函数的和、差、积、商的求导法则 2.2 反函数的求导法则 2.3 复合函数的求导法则 2.4 基本求导法则与导数公式 三…