当前位置: 首页 > 科技观察

从lsof入手,深入了解Linux虚拟文件系统的背景

时间:2023-03-13 02:12:12 科技观察

有时候会出现这样的情况,磁盘空间显示已经满了,但是在查看磁盘的具体文件使用情况时,发现磁盘仍然有很多可用空间。1、执行df命令查看磁盘使用情况,发现磁盘已满。-bash-4.2$df-ThFilesystemTypeSizeUsedAvailUse%Mountedon/dev/vda1ext430G30G0100%/devtmpfsdevtmpfs489M0489M0%/devtmpfstmpfs497M0497M0%/dev/shmtmpfstmpfs497M50M447M11%/runtmpfstmpfs497M0497M0%/sys/fs/cgroup2.执行du命令查看各个目录的磁盘占用情况,把各个目录文件大小加起来,发现磁盘没有满,莫名少了10多G的空间。-bash-4.2$du-h--max-depth=1/home16M/home/logs11G/home/serverdog11G/home3.为什么会这样?因为虽然文件已经被删除,但是一些进程仍然打开着这些文件,所以它们占用的磁盘空间并没有被释放。执行lsof命令以显示打开的已删除文件。重新启动(或清除)有问题的进程,磁盘空间将被释放。-bash-4.2#lsof|grepdeletemysqld2470mysql4uREG253,10523577/var/tmp/ibfTeQFn(deleted)mysqld2470mysql5uREG253,10523579/var/tmp/ibaHcIdW(deleted)mysqld2470mysql6uREG253,10523581/var/tmp/ibLjiALu(deleted)mysqld2470mysql7uREG253,10523585/var/tmp/ibCFnzTB(deleted)mysqld2470mysql11uREG253,10523587/var/tmp/ibCjuqva(deleted)那么,Linux文件系统为什么要这样设计呢?要理解这一点,首先要搞清楚并不容易。下面将从一些基本概念入手,一步步梳理:什么是虚拟文件系统(VFS:virtualfilesystem)?什么是通用文档模型?Superblock对象、inode对象、文件对象、dentry对象、文件的概念、内存的表示、磁盘的表示、目录树的构建、软链接vs硬链接、文件&磁盘管理inode状态文件&进程管理操作:打开&删除VirtualFilesystem(虚拟文件系统)下图显示了Linux操作系统中负责文件管理的基本组件。上半区是用户态,下半区是内核态。应用程序使用标准库libc访问文件,库将请求映射到系统调用以进入内核模式。所有与文件相关的操作的入口点都是虚拟文件系统(VFS),而不是特定的文件系统(例如Ext3、ReiserFS和NFS)。VFS提供了系统库和特定文件系统之间的接口。所以VFS不仅作为一个抽象层,而且实际上它提供了一个文件系统的基本实现,可以被不同的实现使用和扩展。因此,要了解文件系统的工作原理,必须先了解VFS。通用文件模型VFS的主要思想是引入一个通用文件模型(commonfilemodel)。通用文件模型由以下对象类型组成:超级块对象(superblockobject)内存:文件系统安装时创建,存储文件系统信息磁盘:对应文件系统控制块(filesystemcontrolblock)索引节点存储在磁盘上的对象(inodeobject)内存:在被访问时创建,存储特定文件的一般信息(inode结构)磁盘:对应于存储在磁盘上的一个文件控制块(filecontrolblock)每个inode对象都有一个inodenumber,唯一标识文件系统的文件对象(fileobject)内存:在文件打开时创建,存储打开的文件与进程(文件结构)交互的信息。只有当进程访问内存中的文件时,内核中才会存在打开的文件信息。目录项对象(dentryobject)内存:一旦目录项被读入内存,VFS会将其转换为目录项对象的dentry结构磁盘:特定的文件系统以特定的方式存储在磁盘上(名称)和相关信息目录树链接到对应的文件一般情况下,Linux的根文件系统(system'srootfilessystem)是内核启动挂载的第一个文件系统。内核代码镜像文件存放在根文件系统中,系统启动程序会在根文件系统挂载后加载一些基本的初始化脚本和服务到内存中运行(文件系统和内核是完全独立的两部分).其他文件系统后续通过脚本或命令等方式,作为子文件系统安装到已安装文件系统的目录下,最终形成整个目录树。start_kernel  vfs_caches_init    mnt_init      init_rootfs//注册rootfs文件系统      init_mount_tree//挂载rootfs文件系统  …rest_init  kernel_thread(kernel,CLONE_init);对于单个文件系统而言,在安装文件系统时,会创建一个超级块对象;沿树查找文件时,总是先从第一知识目录中找到匹配的目录项,以获得对应的索引节点,然后读取索引节点的目录文件,转换为dentry对象,然后检查匹配的目录项,重复上述过程,直到找到对应文件的索引节点,创建索引节点对象。软链接vs硬链接软链接是一个普通的文件,它存储了另一个文件的路径名。硬链接指向同一个索引节点,硬链接的个数记录在索引节点对象的i_nlink字段中。当i_nlink字段为零时,表示没有指向该文件的硬链接。文件和进程管理下图是进程如何与文件交互的简单示例。三个不同的进程打开同一个文件,每个进程都有自己的文件对象,其中两个使用同一个硬链接(每个硬链接对应一个目录对象),两个目录项对象都指向同一个索引节点对象。索引节点的数据由两部分组成:内存数据和磁盘数据。Linux使用Writeback作为inode的数据一致性策略。对于索引节点的数据,当文件打开时,索引节点会被加载到内存中;当它不再被进程使用时,就会被踢出内存;如果中间有更新,需要将数据写回磁盘。*"in_use"-validinode,i_count>0,i_nlink>0*"dirty"-as"in_use"butsodirty*"unused"-validinode,i_count=0索引节点是否还在使用是通过open()和close()操作创建和销毁文件对象,文件对象通过索引节点提供的iget和iput更新索引节点的i_count字段,完成使用计数。打开操作将i_count加一,关闭操作将i_count减一。关闭操作时,判断索引节点是否被释放。如果i_count=0,表示没有进程引用,将从内存中释放。文件&磁盘管理文件和磁盘管理最密切相关的操作是touch和rm操作,尤其是后者最为关键。通过strace(或dtruss)查看rm#dtrussrmtmp...geteuid(0x0,0x0,0x0)=00ioctl(0x0,0x4004667A,0x7FFEE06F09C4)=00lstat64("tmp\0",0x7FFEE06F0968,0x0)=的实际系统调用00access("tmp\0",0x2,0x0)=00unlink("tmp\0",0x0,0x0)=00可以发现rm其实是通过unlink来完成的。unlink表示删除目录项并减少其索引节点的数量。按照一般的文件模型,父目录本身也是一个文件,也就是说目录项是其文件数据的一部分。删除目录项相当于删除父目录下文件的数据,即先打开父目录下的文件。那么,删除操作可以理解为:1.删除命令(一个进程)使用open操作获取父目录文件对象2.通过iget增加目录文件的索引节点对象计数3.读取目录文件数据并将目录文件数据转换为目录项对象因为目录项中包含文件的索引节点,同样需要通过iget增加文件的索引节点对象计数4.删除目录的目录项5.减少文件索引节点对象i_nlink的硬链接计数6.结束通过iput的配对文件索引节点对象的操作,使用计数i_count减一判断i_count是否为0,如果为0则释放内??存然后判断i_nlink是否为0,如果为0,则释放磁盘空间7.通过iputObject操作结束目录索引节点。回顾遇到的问题,其实可以从两个角度来理解:索引与数据、文件系统与文件、磁盘管理与文件、进程管理与文件。核心是文件的索引,而不是文件的数据。.将数据与索引分开是理解文件系统的关键。缓存策略由于操作系统采用了Writeback策略,也就是说只有先释放内存才能释放磁盘。为什么是lsof?从上面的模型可以很清楚的理解,因为目录没有索引到文件,但是文件还是有索引到文件的,所以不能立即释放磁盘空间。为什么lsof可以找到已删除但未释放的文件?lsof,顾名思义:listopenfiles,这个命令的原理是查找打开文件列表,这样就可以找到已经删除但没有释放的文件。