千锋扣丁学堂Linux培训之虚拟文件系统详解

2019-05-22 14:13:17 4339浏览

今天千锋扣丁学堂Linux培训老师给大家分享一篇关于Linux中虚拟文件系统的详细介绍,首先虚拟文件系统是一种神奇的抽象,它使得“一切皆文件”哲学在Linux中成为了可能。




什么是文件系统?根据早期的Linux贡献者和作家RobertLove所说,“文件系统是一个遵循特定结构的数据的分层存储。”不过,这种描述也同样适用于VFAT(虚拟文件分配表VirtualFileAllocationTable)、Git和Cassandra(一种NoSQL数据库)。那么如何区别文件系统呢?

文件系统基础概念

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


VFS是著名的类Unix系统中“一切皆文件”概念的基础。让我们看一下它有多奇怪,上面的小小演示体现了字符设备/dev/console实际的工作。该图显示了一个在虚拟电传打字控制台(tty)上的交互式Bash会话。将一个字符串发送到虚拟控制台设备会使其显示在虚拟屏幕上。而VFS甚至还有其它更奇怪的属性。例如,它可以在其中寻址。

我们熟悉的文件系统如ext4、NFS和/proc都在名为file_operations的C语言数据结构中提供了三大函数的定义。此外,个别的文件系统会以熟悉的面向对象的方式扩展和覆盖了VFS功能。正如RobertLove指出的那样,VFS的抽象使Linux用户可以轻松地将文件复制到(或复制自)外部操作系统或抽象实体(如管道),而无需担心其内部数据格式。在用户空间这一侧,通过系统调用,进程可以使用文件系统方法之一read()从文件复制到内核的数据结构中,然后使用另一种文件系统的方法write()输出数据。

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

下图大致说明了用户空间如何访问通常挂载在Linux系统上的各种类型文件系统。像管道、dmesg和POSIX时钟这样的结构在此图中未显示,它们也实现了structfile_operations,而且其访问也要通过VFS层。


VFS是个“垫片层”,位于系统调用和特定file_operations的实现(如ext4和procfs)之间。然后,file_operations函数可以与特定于设备的驱动程序或内存访问器进行通信。tmpfs、devtmpfs和cgroup不使用file_operations而是直接访问内存。

VFS的存在促进了代码重用,因为与文件系统相关的基本方法不需要由每种文件系统类型重新实现。代码重用是一种被广泛接受的软件工程最佳实践!唉,但是如果重用的代码引入了严重的错误,那么继承常用方法的所有实现都会受到影响。

/tmp:一个小提示

找出系统中存在的VFS的简单方法是键入mount|grep-vsd|grep-v:/,在大多数计算机上,它将列出所有未驻留在磁盘上,同时也不是NFS的已挂载文件系统。其中一个列出的VFS挂载肯定是/tmp,对吧?

为什么把/tmp留在存储设备上是不可取的?因为/tmp中的文件是临时的(!),并且存储设备比内存慢,所以创建了tmpfs这种文件系统。此外,比起内存,物理设备频繁写入更容易磨损。最后,/tmp中的文件可能包含敏感信息,因此在每次重新启动时让它们消失是一项功能。

不幸的是,默认情况下,某些Linux发行版的安装脚本仍会在存储设备上创建/tmp。如果你的系统出现这种情况,请不要绝望。按照一直优秀的ArchWiki上的简单说明来解决问题就行,记住分配给tmpfs的内存就不能用于其他目的了。换句话说,包含了大文件的庞大的tmpfs可能会让系统耗尽内存并崩溃。

另一个提示:编辑/etc/fstab文件时,请务必以换行符结束,否则系统将无法启动。(猜猜我怎么知道。)

/proc和/sys

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

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


/proc文件的行为说明了VFS可以与磁盘上的文件系统不同。一方面,/proc/meminfo包含了可由命令free展现出来的信息。另一方面,它还是空的!怎么会这样?这种情况让人联想起康奈尔大学物理学家N.DavidMermin在1985年写的一篇名为《没有人看见月亮的情况吗?现实和量子理论》。事实是当进程从/proc请求数据时内核再收集有关内存的统计信息,而且当没有人查看它时,/proc中的文件实际上没有任何内容。正如Mermin所说,“这是一个基本的量子学说,一般来说,测量不会揭示被测属性的预先存在的价值。”(关于月球的问题的答案留作练习。)

procfs的空文件是有道理的,因为那里可用的信息是动态的。sysfs的情况则不同。让我们比较一下/proc与/sys中不为空的文件数量。



procfs只有一个不为空的文件,即导出的内核配置,这是一个例外,因为每次启动只需要生成一次。另一方面,/sys有许多更大一些的文件,其中大多数由一页内存组成。通常,sysfs文件只包含一个数字或字符串,与通过读取/proc/meminfo等文件生成的信息表格形成鲜明对比。

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

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



sysfs中的文件确切地描述了实体的每个属性,并且可以是可读的、可写的,或两者兼而有之。文件中的“0”表示SSD不可移动的存储设备。

sysfs中的文件确切地描述了实体的每个属性,并且可以是可读的、可写的,或两者兼而有之。文件中的“0”表示SSD不可移动的存储设备。

用eBPF和bcc工具一窥VFS内部

了解内核如何管理sysfs文件的最简单方法是观察它的运行情况,在ARM64或x86_64上观看的最简单方法是使用eBPF。eBPF(扩展的伯克利数据包过滤器extendedBerkeleyPacketFilter)由在内核中运行的虚拟机组成,特权用户可以从命令行进行查询。内核源代码告诉读者内核可以做什么;而在一个启动的系统上运行eBPF工具会显示内核实际上做了什么。

令人高兴的是,通过bcc工具入门使用eBPF非常容易,这些工具在主要Linux发行版的软件包中都有,并且已经由BrendanGregg给出了充分的文档说明。bcc工具是带有小段嵌入式C语言片段的Python脚本,这意味着任何对这两种语言熟悉的人都可以轻松修改它们。据当前统计,bcc/tools中有80个Python脚本,使得系统管理员或开发人员很有可能能够找到与她/他的需求相关的已有脚本。

要了解VFS在正在运行中的系统上的工作情况,请尝试使用简单的vfscount或vfsstat脚本,这可以看到每秒都会发生数十次对vfs_open()及其相关的调用。


作为一个不太重要的例子,让我们看一下在运行的系统上插入USB记忆棒时sysfs中会发生什么。


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

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

无论如何,events文件的目的是什么?使用cscope查找函数__device_add_disk()显示它调用disk_add_events(),并且可以将“mediachange”或“ejectrequest”写入到该文件。这里,内核的块层通知用户空间该“磁盘”的出现和消失。考虑一下这种检查USB棒的插入的工作原理的方法与试图仅从源头中找出该过程的速度有多快。

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

确实,没有人通过拔出电源插头来关闭服务器或桌面系统。为什么?因为物理存储设备上挂载的文件系统可能有挂起的(未完成的)写入,并且记录其状态的数据结构可能与写入存储器的内容不同步。当发生这种情况时,系统所有者将不得不在下次启动时等待fsck文件系统恢复工具运行完成,在最坏的情况下,实际上会丢失数据。

然而,狂热爱好者会听说许多物联网和嵌入式设备,如路由器、恒温器和汽车现在都运行着Linux。许多这些设备几乎完全没有用户界面,并且没有办法干净地让它们“解除启动”。想一想启动电池耗尽的汽车,其中运行Linux的主机设备的电源会不断加电断电。当引擎最终开始运行时,系统如何在没有长时间fsck的情况下启动呢?答案是嵌入式设备依赖于只读根文件系统(简称ro-rootfs)。

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

对于嵌入式开发人员,创建只读根文件系统确实需要做一些额外的工作,而这正是VFS的用武之地。Linux需要/var中的文件可写,此外,嵌入式系统运行的许多流行应用程序会尝试在$HOME中创建配置的点文件。放在家目录中的配置文件的一种解决方案通常是预生成它们并将它们构建到rootfs中。对于/var,一种方法是将其挂载在单独的可写分区上,而/本身以只读方式挂载。使用绑定或叠加挂载是另一种流行的替代方案。

绑定和叠加挂载以及在容器中的使用

运行manmount是了解绑定挂载bindmount和叠加挂载overlaymount的最好办法,这种方法使得嵌入式开发人员和系统管理员能够在一个路径位置创建文件系统,然后以另外一个路径将其提供给应用程序。对于嵌入式系统,这代表着可以将文件存储在/var中的不可写闪存设备上,但是在启动时将tmpfs中的路径叠加挂载或绑定挂载到/var路径上,这样应用程序就可以在那里随意写它们的内容了。下次加电时,/var中的变化将会消失。叠加挂载为tmpfs和底层文件系统提供了联合,允许对ro-rootfs中的现有文件进行直接修改,而绑定挂载可以使新的空tmpfs目录在ro-rootfs路径中显示为可写。虽然叠加文件系统是一种适当的文件系统类型,而绑定挂载由VFS命名空间工具实现的。

根据叠加挂载和绑定挂载的描述,没有人会对Linux容器中大量使用它们感到惊讶。让我们通过运行bcc的mountsnoop工具监视当使用systemd-nspawn启动容器时会发生什么:


让我们看看发生了什么:


这里,systemd-nspawn将主机的procfs和sysfs中的选定文件按其rootfs中的路径提供给容器。除了设置绑定挂载时的MS_BIND标志之外,mount系统调用的一些其它标志用于确定主机命名空间和容器中的更改之间的关系。例如,绑定挂载可以将/proc和/sys中的更改传播到容器,也可以隐藏它们,具体取决于调用。

理解Linux内部结构看似是一项不可能完成的任务,因为除了Linux用户空间应用程序和glibc这样的C库中的系统调用接口,内核本身也包含大量代码。取得进展的一种方法是阅读一个内核子系统的源代码,重点是理解面向用户空间的系统调用和头文件以及主要的内核内部接口,这里以file_operations表为例。file_operations使得“一切都是文件”得以可以实际工作,因此掌握它们收获特别大。顶级fs/目录中的内核C源文件构成了虚拟文件系统的实现,虚拟文件??系统是支持流行的文件系统和存储设备的广泛且相对简单的互操作性的垫片层。通过Linux命名空间进行绑定挂载和覆盖挂载是VFS魔术,它使容器和只读根文件系统成为可能。结合对源代码的研究,eBPF内核工具及其bcc接口使得探测内核比以往任何时候都更简单。

以上就是关于千锋扣丁学堂Linux培训之虚拟文件系统的全部内容,最后想要了解更多关于Linux方面内容的小伙伴,请关注扣丁学堂Linux培训官网、微信等平台,扣丁学堂IT职业在线学习教育平台为您提供权威的Linux开发视频,Linux培训后的前景无限,行业薪资和未来的发展会越来越好的,扣丁学堂老师精心推出的Linux视频教程定能让你快速掌握Linux从入门到精通开发实战技能。扣丁学堂Linux技术交流群:422345477。


扣丁学堂微信公众号                          Python全栈开发爬虫人工智能机器学习数据分析免费公开课直播间


      【关注微信公众号获取更多学习资料】         【扫码进入Python全栈开发免费公开课】



查看更多关于“Linux培训资讯”的相关文章>>

标签: Linux培训 Linux视频教程 红帽Linux视频 Linux学习视频 Linux入门视频

热门专区

暂无热门资讯

课程推荐

微信
微博
15311698296

全国免费咨询热线

邮箱:codingke@1000phone.com

官方群:148715490

北京千锋互联科技有限公司版权所有   北京市海淀区宝盛北里西区28号中关村智诚科创大厦4层
京ICP备12003911号-6   Copyright © 2013 - 2019

京公网安备 11010802030908号