linux虚拟文件系统_Linux中的虚拟文件系统:为什么需要它们以及它们如何工作...

linux虚拟文件系统

什么是文件系统? 早期的Linux贡献者和作者Robert Love表示 ,“文件系统是依托特定结构的分层数据存储。” 但是,此描述同样适用于VFAT(虚拟文件分配表),Git和CassandraNoSQL数据库 )。 那么文件系统有什么区别呢?

文件系统基础

Linux内核要求实体要成为文件系统,它还必须在具有与之关联的名称的持久对象上实现open()read()write()方法。 从面向对象编程的角度来看,内核将通用文件系统视为抽象接口,并且这三大函数是“虚拟的”,没有默认定义。 因此,内核的默认文件系统实现称为虚拟文件系统(VFS)。

If we can open(), close(), read() and write(), it is a file as this console session shows.
如果我们可以open(),read()和write(),则它是此控制台会话显示的文件。

VFS是著名的观点,即在类Unix系统中“一切都是文件”。 考虑一下上面带有字符设备/ dev / console的微型演示实际上是多么奇怪。 该图显示了在虚拟电传打字机(tty)上的交互式Bash会话。 将字符串发送到虚拟控制台设备会使它出现在虚拟屏幕上。 VFS具有其他甚至更奇怪的属性。 例如, 可以在其中寻找

file_operations 。 另外,特定的文件系统以熟悉的面向对象的方式扩展和覆盖VFS功能。 正如罗伯特·洛夫(Robert Love)指出的那样,VFS的抽象使Linux用户可以轻松地在外部操作系统或抽象实体(如管道)之间来回复制文件,而不必担心其内部数据格式。 代表用户空间,通过系统调用,进程可以使用一个文件系统的read()方法从文件复制到内核的数据结构中,然后使用另一种文件系统的write()方法输出数据。

属于VFS基本类型本身的功能定义可在内核源代码的fs / *。c文件中找到,而fs /的子目录则包含特定的文件系统。 内核还包含类似文件系统的实体,例如cgroups,/ dev和tmpfs,它们在引导过程的早期就需要,因此在内核的init /子目录中定义。 请注意,cgroup,/ dev和tmpfs不会调用file_operations大三函数,而是直接从内存中读取和写入。

下图大致说明了用户空间如何访问通常安装在Linux系统上的各种类型的文件系统。 未显示的是类似管道,dmesg和POSIX时钟的构造,它们也实现了struct file_operations,因此其访问通过VFS层进行。

How userspace accesses various types of filesystems
VFS是系统调用和特定文件操作(例如ext4和)的实现者之间的“中介层” Craft.io 然后,file_operations函数可以与特定于设备的驱动程序或与内存访问器进行通信。 tmpfs devtmpfs 小组 不要使用file_operations,而是直接访问内存。

VFS的存在促进了代码重用,因为与文件系统相关联的基本方法不需要由每种文件系统类型重新实现。 代码重用是被广泛接受的软件工程最佳实践! las,如果重用的代码引入了严重的错误 ,那么所有继承通用方法的实现都将遭受它们的困扰。

/ tmp:简单提示

找出系统上存在哪些VFS的一种简单方法是键入mount | grep -v sd | grep -v:/ ,它将列出所有不在磁盘上且在大多数计算机上都不是NFS的已挂载文件系统。 列出的VFS挂载之一肯定是/ tmp,对吗?

Man with shocked expression
每个人都知道保持/ tmp 在物理存储设备上太疯狂了! 信用: https//tinyurl.com/ybomxyfo

为什么不建议在存储中使用/ tmp? 因为/ tmp中的文件是临时文件(!),并且存储设备比创建tmpfs的内存慢。 此外,物理设备比存储器更容易遭受频繁写入的磨损。 最后,/ tmp中的文件可能包含敏感信息,因此使它们在每次重新启动时消失都是一项功能。

不幸的是,默认情况下,某些Linux发行版的安装脚本仍在存储设备上创建/ tmp。 如果您的系统属于这种情况,请不要感到绝望。 请遵循始终出色的Arch Wiki上的简单说明来解决此问题,请记住分配给tmpfs的内存不能用于其他目的。 换句话说,具有巨大tmpfs且其中包含大文件的系统可能会耗尽内存并崩溃。 另一个提示:编辑/ etc / fstab文件时,请确保以换行符结尾,否则系统将无法启动。 (猜猜我怎么知道。)

/ proc和/ sys

除了/ tmp之外,大多数Linux用户最熟悉的VFS是/ proc和/ sys。 (/ dev依赖共享内存,没有file_operations)。 为什么有两种口味? 让我们更详细地看看。

procfs提供了有关内核及其控制的用户空间进程的瞬时状态的快照。 在/ proc中,内核发布有关其提供的功能的信息,例如中断,虚拟内存和调度程序。 另外,/ proc / sys是用户空间可以访问通过sysctl命令配置的设置的位置。 在/ proc / <PID>目录中报告有关单个进程的状态和统计信息。

Console
/ proc / 记忆信息 是一个空文件,其中仍然包含有价值的信息。

/ proc文件的行为说明了VFS与磁盘文件系统的不同之处。 一方面,/ proc / meminfo包含由free命令提供的信息。 另一方面,它也是空的! 怎么会这样? 这种情况使人想起了康奈尔大学物理学家N. David Mermin在1985年写的一篇著名文章,题为“ 当没有人看时,月亮在那里吗?现实与量子理论”。 事实是,有关内存的内核收集统计信息当一个进程从/ proc要求他们,其实没有什么文件在/ proc时,没有人期待。 正如Mermin所说 :“这是一种基本的量子学说,一般而言,测量不会揭示所测量属性的预先存在的值。” (有关月球的问题的答案留作练习。)

Full moon
当没有进程访问它们时,/ proc中的文件为空。 来源

由于可用的信息是动态的,procfs的明显空虚是有道理的。 sysfs的情况有所不同。 让我们比较一下/ proc和/ sys中有多少个至少一个字节大小的文件。

Console

Procfs恰好具有一种,即导出的内核配置,这是一个例外,因为它每次引导只需生成一次。 另一方面,/ sys有很多较大的文件,其中大多数包含一页内存。 通常,sysfs文件仅包含一个数字或字符串,这与通过读取/ proc / meminfo之类的文件所生成的信息表相反。

sysfs的目的是向用户空间公开内核称为“ kobjects”的可读和可写属性。 kobject的唯一用途是引用计数:删除对kobject的最后一个引用时,系统将回收与之关联的资源。 但是,/ sys构成了内核中最著名的“ 对用户空间稳定的ABI,在任何情况下都没有人会“中断”。 这并不意味着sysfs中的文件是静态的,这与易失性对象的引用计数是相反的。

相反,内核的稳定ABI限制了/ sys中可能出现的内容,而不是在任何给定瞬间实际存在的内容。 在sysfs中列出文件的权限,可以使您了解如何设置或读取设备,模块,文件系统等的可配置,可调参数。 逻辑上得出这样的结论,即procfs也是内核稳定的ABI的一部分,尽管内核的文档没有明确说明。

Console
文件中 系统文件 分别为一个实体描述一个属性,并且该属性可以是可读的,可写的,或者两者都可以。 文件中的“ 0”表明该SSD无法移动。

使用eBPF和bcc工具侦听VFS

了解内核如何管理sysfs文件的最简单方法是观察它的运行情况,而在ARM64或x86_64上观察的最简单方法是使用eBPF。 eBPF(扩展的Berkeley数据包筛选器)由运行在内核中的虚拟机组成,特权用户可以从命令行查询该虚拟机 。 内核源代码告诉读者内核可以做什么; 在引导的系统上运行eBPF工具将显示内核实际执行的操作

令人高兴的是,通过bcc工具可以很容易地开始使用eBPF, bcc工具可以从主要Linux发行版中获得,并且由Brendan Gregg进行了充分记录 。 bcc工具是带有小型嵌入式C语言片段的Python脚本,这意味着任何一种使用这两种语言的人都可以轻松地对其进行修改。 如此算来, bcc / tools中有80个Python脚本 ,这使得系统管理员或开发人员很可能会找到与她/他的需求相关的现有脚本

要对VFS在正在运行的系统上执行的工作有一个非常粗略的了解,请尝试使用简单的vfscountvfsstat ,它们显示每秒对vfs_open()及其朋友的数十次调用。

Console - vfsstat.py
vfsstat.py是带有嵌入式C代码段的Python脚本,该代码段仅计算VFS函数调用。

举一个简单的例子,让我们看一下在运行的系统上插入USB记忆棒时sysfs会发生什么。

Console when USB is inserted
使用eBPF观看插入USB记忆棒时/ sys中发生的情况,包括简单和复杂的示例。

在上面的第一个简单示例中,无论何时运行sysfs_create_files()命令, trace.py bcc工具脚本都会打印出一条消息。 我们看到sysfs_create_files()由kworker线程启动以响应USB记忆棒的插入,但是创建了什么文件? 第二个示例说明了eBPF的全部功能。 在这里,trace.py将打印内核回溯(-K选项)以及sysfs_create_files()创建的文件的名称。 单引号内的代码段是一些C源代码,其中包括一个易于识别的格式字符串,所提供的Python脚本可诱导LLVM即时编译器在内核虚拟机内部进行编译和执行。 完整的sysfs_create_files()函数签名必须在第二条命令中重现,以便格式字符串可以引用其中一个参数。 在此C代码段中出错会导致可识别的C编译器错误。 例如,如果省略-I参数,则结果为“无法编译BPF文本”。 精通C或Python的开发人员将发现bcc工具易于扩展和修改。

插入USB记忆棒后,出现内核回溯,显示PID 7711是一个kworker线程,该线程在sysfs中创建了一个名为“事件”的文件。 与sysfs_remove_files()的相应调用显示,删除USB记忆棒会导致事件文件的删除,这与引用计数的想法保持一致。 在插入USB记忆棒(未显示)期间使用eBPF观看sysfs_create_link()会发现创建了不少于48个符号链接。

无论如何,事件文件的目的是什么? 使用cscope查找函数__device_add_disk()会表明它调用disk_add_events(),并且“ media_change”或“ eject_request”都可以写入事件文件。 在这里,内核的块层正在向用户空间通知“磁盘”的出现和消失。 考虑将研究USB记忆棒插入方式的方法与试图仅从源头确定过程的方法相比,能提供多快的信息。

只读根文件系统使嵌入式设备成为可能

可以肯定的是,没有人通过拔出电源插头来关闭服务器或台式机系统。 为什么? 由于物理存储设备上已挂载的文件系统可能具有挂起的写操作,因此记录其状态的数据结构可能会与存储中写的内容不同步。 发生这种情况时,系统所有者将必须在下次启动时等待fsck文件系统恢复工具运行,在最坏的情况下,实际上将丢失数据。

但是,狂热者会听说许多IoT和嵌入式设备(例如路由器,恒温器和汽车)现在都运行Linux。 这些设备中的许多设备几乎完全缺少用户界面,因此无法干净地“卸载”它们。 考虑启动一辆电池没电的汽车,这时运行Linux的主机的电源反复上升和下降。 引擎最终开始运行时,如何在不使用长fsck的情况下启动系统? 答案是嵌入式设备依赖于只读的根文件系统 (简称ro-rootfs)。

Photograph of a console
滚装 根文件系统 是为什么嵌入式系统并不需要经常fsck的。 信用(经许可): https : //tinyurl.com/yxoauoub

ro-rootfs提供了许多优点,而这些优点比廉洁性还不那么明显。 一种是如果没有Linux进程可以在其中将恶意软件写入/ usr或/ lib。 另一个问题是,对于远程设备的现场支持而言,一个基本不变的文件系统至关重要,因为支持人员拥有的本地系统名义上与现场的系统相同。 也许最重要(但也最微妙)的优点是ro-rootfs迫使开发人员在项目的设计阶段决定哪些系统对象将是不可变的。 与ro-rootfs的处理通常不方便甚至痛苦,就像编程语言中的const变量经常这样,但是这样做的好处很容易偿还了额外的开销。

创建只读rootfs确实需要嵌入式开发人员付出额外的努力,这就是VFS的用处。Linux需要/ var中的文件可写,此外,嵌入式系统运行的许多流行应用程序都将尝试创建配置。 $ HOME中的点文件。 主目录中配置文件的一种解决方案通常是预先生成它们并将其构建到rootfs中。 对于/ var,一种方法是将/ mount挂载到单独的可写分区上,而/本身以只读方式挂载。 使用绑定或覆盖挂载是另一种流行的选择。

绑定和叠加安装及其在容器中的使用

运行man挂载是了解绑定和覆盖挂载的最佳场所,它使嵌入式开发人员和系统管理员可以在一个路径位置创建文件系统,然后在第二个路径将其提供给应用程序。 对于嵌入式系统,这意味着可以将文件存储在/ var上不可写的闪存设备上,但可以在启动时将tmpfs中的路径覆盖或绑定安装到/ var路径上,以便应用程序可以将其爬到自己的心脏喜。 下次开机时,/ var中的更改将消失。 覆盖挂载提供tmpfs与基础文件系统之间的联合,并允许对ro-rootfs中的现有文件进行明显修改,而bind挂载可以使新的空tmpfs目录在ro-rootfs路径上显示为可写。 尽管overlayfs是适当的文件系统类型,但绑定挂载由VFS命名空间工具实现

根据对overlay和bind挂载的描述, Linux容器对它们的大量使用不会让人感到惊讶。 让我们窥探当我们通过运行bcc的mountsnoop工具使用systemd-nspawn启动容器时会发生什么:

Console - system-nspawn invocation
系统- nspawn 调用会在mountsnoop.py运行时启动容器。

让我们看看发生了什么:

Console - Running mountsnoop
跑步 mountsnoop 在容器“启动”期间,发现容器运行时严重依赖于绑定安装。 (仅显示冗长的输出的开头)

在这里,systemd-nspawn将主机的procfs和sysfs中的选定文件提供给容器在其rootfs中的路径。 除了设置绑定安装的MS_BIND标志外,“ mount”系统调用调用的其他一些标志还确定主机名称空间和容器中的更改之间的关系。 例如,根据调用的不同,绑定安装可以将/ proc和/ sys中的更改传播到容器,也可以隐藏它们。

摘要

了解Linux内部似乎是不可能完成的任务,因为内核本身包含大量的代码,而在诸如glibc之类的C库中却遗留了Linux用户空间应用程序和系统调用接口。 取得进展的一种方法是阅读一个内核子系统的源代码,重点是理解面向用户空间的系统调用和标头以及主要的内核内部接口,在此以file_operations表为例。 文件操作实际上是使“一切都是文件”起作用的原因,因此获得对它们的句柄特别令人满意。 顶级fs /目录中的内核C源文件构成其虚拟文件系统的实现,虚拟文件​​系统是使常用文件系统和存储设备具有广泛且相对直接的互操作性的垫片层。 通过Linux名称空间进行的绑定和覆盖挂载是VFS的魔力,它使容器和只读根文件系统成为可能。 结合对源代码的研究,eBPF内核工具及其bcc接口使探测内核比以往任何时候都更加简单。

非常感谢Akkana PeckMichael Eager的评论和更正。


Alison Chaiken将在3月7日至10日于加利福尼亚州帕萨迪纳举行的第17届年度南加州Linux博览会( SCaLE 17x )上介绍虚拟文件系统:我们为什么需要它们以及它们如何工作

翻译自: https://opensource.com/article/19/3/virtual-filesystems-linux

linux虚拟文件系统

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值