Linux 从 lsof 开始,深入理解 Linux 虚拟文件系统!

背景

有时会出现这样的情况,磁盘空间显示已经被占满,但是在查看磁盘的具体文件占用情况时,发现磁盘仍然有很大的空余空间。

1. 执行df命令查看磁盘使用情况,发现磁盘已经满了。

-bash-4.2$ df -Th
Filesystem     Type      Size  Used Avail Use% Mounted on
/dev/vda1      ext4       30G    30G 0      100% /
devtmpfs       devtmpfs  489M     0  489M   0% /dev
tmpfs          tmpfs     497M     0  497M   0% /dev/shm
tmpfs          tmpfs     497M   50M  447M  11% /run
tmpfs          tmpfs     497M     0  497M   0% /sys/fs/cgroup

2. 执行du命令查看各个目录的磁盘占用情况,把各个目录文件的大小相加,发现并没有占满磁盘,有10多G空间莫名失踪。

-bash-4.2$ du -h --max-depth=1 /home
16M /home/logs
11G /home/serverdog
11G /home

3. 为何会出现这样的情况呢?

因为虽然文件已被删除,但是一些进程仍然打开这些文件,因此其占用的磁盘空间并没有被释放。执行 lsof 命令显示打开已删除的文件。将有问题的进程重启(或,清空),磁盘空间就会得到释放。

-bash-4.2# lsof | grep delete
mysqld     2470         mysql    4u      REG              253,1           0     523577 /var/tmp/ibfTeQFn (deleted)
mysqld     2470         mysql    5u      REG              253,1           0     523579 /var/tmp/ibaHcIdW (deleted)
mysqld     2470         mysql    6u      REG              253,1           0     523581 /var/tmp/ibLjiALu (deleted)
mysqld     2470         mysql    7u      REG              253,1           0     523585 /var/tmp/ibCFnzTB (deleted)
mysqld     2470         mysql   11u      REG              253,1           0     523587 /var/tmp/ibCjuqva (deleted)

那么,Linux的文件系统,到底为什么这么设计呢?要了解这些,就要先弄清楚并不容易,下面将从一些基本概念入手,一步步将这些梳理清楚:

什么是虚拟文件系统(VFS:virtual filesystem)?


下图显示了Linux操作系统中负责文件管理的基本组件。上半区域为用户模式,下半区域为内核模式。应用程序使用标准库libc来访问文件,库将请求映射到系统调用,以便进入内核模式。

Linux 从 lsof 开始,深入理解 Linux 虚拟文件系统!

所有与文件相关的操作的入口都是虚拟文件系统(VFS),而非特定的额文件系统(如Ext3、ReiserFS和NFS)。VFS 提供了系统库和特定文件系统之间的接口。因此,VFS 不仅充当抽象层,而且实际上它提供了一个文件系统的基本实现,可以由不同的实现来使用和扩展。因此,要了解文件系统是如何工作的,就要先了解VFS 。

通用文件模型


VFS 的主要思想在于引入了一个通用文件模型(common file model)。通用文件模型由以下对象类型组成:

  • 超级块对象(superblock object)内存:文件系统安装时创建,存放文件系统的有关信息磁盘:对应于存放在磁盘上的文件系统控制块(filesystem control block)
  • 索引节点对象(inode object)内存:访问时创建,存放关于具体文件的一般信息(inode 结构)磁盘:对应于存放在磁盘上的文件控制块(file control block)每个索引节点对象都有一个索引节点号,唯一地标识文件系统的文件
  • 文件对象(file object)内存:打开文件时创建,存放 打开文件 与进程之间进行交互的有关信息(file 结构)打开文件信息,仅当进程访问文件期间存在于内核内存中。
  • 目录项对象(dentry object)内存:目录项一旦被读入内存,VFS就会将其转换成 dentry 结构 的目录项对象磁盘:特定文件系统以特定的方式存储在磁盘上存放目录项(即,文件名称)与对应文件进行链接的有关信息

 

目录树


综合来说,Linux 的 根文件系统(system's root filessystem) 是内核启动mount的第一个文件系统。内核代码映像文件保存在根文件系统中,而系统引导启动程序会在根文件系统挂载之后,从中把一些基本的初始化脚本和服务等加载到内存中去运行(文件系统和内核是完全独立的两个部分)。其他文件系统,则后续通过脚本或命令作为子文件系统安装在已安装文件系统的目录上,最终形成整个目录树。

start_kernel 
  vfs_caches_init 
    mnt_init 
      init_rootfs     // 注册rootfs文件系统
      init_mount_tree // 挂载rootfs文件系统 
  … 
  rest_init 
  kernel_thread(kernel_init, NULL, CLONE_FS);

就单个文件系统而言,在文件系统安装时,创建超级块对象;沿树查找文件时,总是首先从初识目录的中查找匹配的目录项,以便获取相应的索引节点,然后读取索引节点的目录文件,转化为dentry对象,再检查匹配的目录项,反复执行以上过程,直至找到对应的文件的索引节点,并创建索引节点对象。

软链接 vs 硬链接


软链接是一个普通的文件,其中存放的是另外一个文件的路径名。硬链接则指向同一个索引节点,硬链接数记录在索引节点对象的 i_nlink 字段。当 i_nlink字段为零时,说明没有硬链接指向该文件。

文件 & 进程管理


下图是一个简单示例,说明进程是怎样与文件进行交互。三个不同进程打开同一个文件,每个进程都有自己的文件对象,其中两个进程使用同一个硬链接(每个硬链接对应一个目录对象),两个目录项对象都指向同一个 索引节点对象。

Linux 从 lsof 开始,深入理解 Linux 虚拟文件系统!

索引节点的数据又由两部分组成:内存数据和磁盘数据。Linux 使用 Write back 作为索引节点的数据一致性策略。对于索引节点的数据,当文件被打开时,才会加载索引节点到内存;当不再被进程使用,则从内存踢出;如果中间有更新,则需要把数据写回磁盘。

*  "in_use" - valid inode, i_count > 0, i_nlink > 0
*  "dirty"  - as "in_use" but also dirty
*  "unused" - valid inode, i_count = 0

索引节点是否仍在使用,是通过 open() 和 close() 操作建立和销毁文件对象,文件对象通过索引节点提供的 iget 和 iput 更新索引节点的i_count字段,以完成使用计数。open 操作使得 i_count 加一, close 操作使得 i_count 减一。在 close 操作时判断索引节点是否释放,如果 i_count = 0,则意味着不再有进程引用,将会从内存释放。

 

文件 & 磁盘管理


文件与磁盘管理联系最紧密的操作,莫过于 touch 和 rm 操作,而尤以后者最为关键。通过strace(或 dtruss),查看 rm 的实际的系统调用


# dtruss rm tmp
...
geteuid(0x0, 0x0, 0x0)       = 0 0
ioctl(0x0, 0x4004667A, 0x7FFEE06F09C4)       = 0 0
lstat64("tmp\0", 0x7FFEE06F0968, 0x0)        = 0 0
access("tmp\0", 0x2, 0x0)        = 0 0
unlink("tmp\0", 0x0, 0x0)        = 0 0

可以发现 rm 实际是通过 unlink 完成的。 unlink代表删除目录项,以及减少其索引节点的计数。由通用文件模型可知,父目录本身同样是一个文件,也就意味着目录项是其文件数据的一部分。删除目录项等价于从父目录的文件中删除数据,也就意味着首先要打开父目录的文件。那么,删除操作即可理解为:

  1. 删除命令(一个进程)使用 open 操作获得父目录文件对象
  2. 通过 iget 增加 目录文件的索引节点对象计数
  3. 读取目录文件数据
    1. 将目录文件数据转化为目录项对象
    2. 由于目录项包含文件的索引节点,类似的,需要通过 iget 增加文件的索引节点对象计数
  4. 删除目录的目录项
  5. 减少文件索引节点对象的硬链接计数i_nlink
  6. 通过 iput 结束对文件索引节点对象的操作,使用计数 i_count 减一
    1. 判断i_count是否为零,如果为零,则释放内存
    2. 然后,判断i_nlink是否为零,如果为零,则释放磁盘空间
  7. 通过 iput 结束对目录索引节点对象的操作。

 

总结


回头来看遇到的问题,其实可以从两个角度来理解:

索引与数据

文件系统与文件、磁盘管理与文件、进程管理与文件,最核心的都是文件的索引,而不是文件的数据。把数据和索引分开是理解文件系统的关键。

Linux 从 lsof 开始,深入理解 Linux 虚拟文件系统!

一个数据构建了各式各样的索引,只有索引全部失效,此时数据才失去意义,此时才能释放

缓存策略

由于操作系统使用 Write back 的策略,意味着只有先释放内存,才有可能释放磁盘。

Why lsof ?

从上面的模型可以很清楚的理解,因为目录已经没有索引到文件了,但是打开文件还有索引到文件,所以不能立刻释放磁盘空间。

为什么 lsof 可以找到已删除未释放的文件呢?
lsof,顾名思义:list open files,该命令的原理就是查找打开文件的列表,因此可以找到已删除未释放的文件。

文章来源:https://www.cnaaa.net,转载请注明出处:https://www.cnaaa.net/archives/11314

(0)
凯影的头像凯影
上一篇 2024年2月19日 下午4:12
下一篇 2024年2月21日 下午4:12

相关推荐

  • 通过远程桌面连接Windows系统的ECS实例提示“你的远程桌面许可证出现问题,您的会话将在60分钟后断开连接。”错误怎么办?

    问题描述 重启Windows系统的ECS实例之后,通过远程桌面连接实例时提示如下错误。 问题原因 Windows系统的ECS实例的本地组策略编辑器中“指定 RD 会话主机服务器的授权模式”的选项设置不为按设备模式。 解决方案

    2023年11月15日
    96700
  • 解决crond引发大量sendmail、postdrop进程导致内存使用过大问题

    现象:客户反馈机器卡顿,上面只运行了一个宝塔,疑似中了木马 分析过程:top发现内存使用过高,ps -aux发现大量postdrop、sendmail、crond进程,而postdrop是由sendmail启动的,sendmail是由crond启动的。 crond在执行脚本时会将脚本输出信息以邮件的形式发送给系统用户,所以必然要调用sendmail,而sen…

    2022年12月6日
    1.5K00
  • CentOS添加永久路由方式

    1. CentOS7添加永久路由   ● 方式一:  执行以下命令打开路由配置文件进行编辑: 这里的<interface>是指您要添加路由的网络接口,例如eth0或ens33  在文件中添加路由规则,例如: 其中<gateway>是默认网关的 IP 地址,<interface>是网络接口的名称  例如: 保存并关闭文件,执行以下命令重启网络…

    2023年12月5日
    62800
  • Linux firewall防火墙 换成 iptables 防火墙

    一、firewalld 增加开放端口 重启防火墙 二、iptables 增加开放端口 如果要修改防火墙配置,如增加防火墙端口3306 增加规则 保存退出后 最后重启系统使设置生效即可。 三、将firewalld防火墙换成iptables 1、直接关闭防火墙 2、设置 iptables service 如果要修改防火墙配置,如增加防火墙端口3306 增加规则 …

    2023年8月9日
    97000
  • Linux Top 命令指南

    top 命令允许用户监视 Linux 上的进程和系统资源使用情况,它是系统管理员工具箱中最有用的工具之一,并且在每个发行版中都预装了它。与 ps 等其他命令不同,它是交互式的,我们可以浏览进程列表、终止进程,等等。本文中,我们将了解如何使用 top 命令。 Getting started top 命令非常简单,只需要在终端中输入 top 即可。top 指令将…

    2022年12月13日
    76300

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

在线咨询: QQ交谈

邮件:712342017@qq.com

工作时间:周一至周五,8:30-17:30,节假日休息

关注微信