| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 系统运维 -> 不知道Linux文件系统是怎么工作的?详解来了 -> 正文阅读 |
|
[系统运维]不知道Linux文件系统是怎么工作的?详解来了 |
和 CPU、内存一样,磁盘和文件系统的管理,也是操作系统最核心的功能。
那么,磁盘和文件系统是怎么工作的呢?又有哪些指标可以衡量它们的性能呢? 索引节点和目录项文件系统,本身是对存储设备上的文件,进行组织管理的机制。组织方式不同,就会形成不同的文件系统。 我们要记住最重要的一点,在 Linux 中一切皆文件。不仅普通的文件和目录,就连块设备、 套接字、管道等,也都要通过统一的文件系统来管理。 为了方便管理,Linux 文件系统为每个文件都分配两个数据结构,索引节点(index node)和目录项(directory entry)。它们主要用来记录文件的元信息和目录结构。
换句话说,索引节点是每个文件的唯一标志,而目录项维护的正是文件系统的树状结构。目录项和索引节点的关系是多对一,你可以简单理解为,一个文件可以有多个别名。 举个例子,通过硬链接为文件创建的别名,就会对应不同的目录项,不过这些目录项本质上还是链接同一个文件,所以,它们的索引节点相同。 索引节点和目录项纪录了文件的元数据,以及文件间的目录关系,那么具体来说,文件数据到底是怎么存储的呢?是不是直接写到磁盘中就好了呢? 实际上,磁盘读写的最小单位是扇区,然而扇区只有 512B 大小,如果每次都读写这么小的单位,效率一定很低。所以,文件系统又把连续的扇区组成了逻辑块,然后每次都以逻 辑块为最小单元,来管理数据。常见的逻辑块大小为 4KB,也就是由连续的 8 个扇区组成。 为了帮助我们理解目录项、索引节点以及文件数据的关系,画了一张示意图。我们可以对照着这张图,来回忆刚刚讲过的内容,把知识和细节串联起来。 不过,这里有两点需要我们注意: 第一,目录项本身就是一个内存缓存,而索引节点则是存储在磁盘中的数据。在前面的 Buffer 和 Cache 原理中,我曾经提到过,为了协调慢速磁盘与快速 CPU 的性能差异,文 件内容会缓存到页缓存 Cache 中。那么,我们也应该想到,这些索引节点自然也会缓存到内存中,加速文件的访问。 第二,磁盘在执行文件系统格式化时,会被分成三个存储区域,超级块、索引节点区和数 据块区。其中, 超级块,存储整个文件系统的状态。 索引节点区,用来存储索引节点。 数据块区,则用来存储文件数据。 虚拟文件系统目录项、索引节点、逻辑块以及超级块,构成了 Linux 文件系统的四大基本要素。不过, 为了支持各种不同的文件系统,Linux 内核在用户进程和文件系统的中间,又引入了一个抽象层,也就是虚拟文件系统 VFS(Virtual File System)。 VFS 定义了一组所有文件系统都支持的数据结构和标准接口。这样,用户进程和内核中的其他子系统,只需要跟 VFS 提供的统一接口进行交互就可以了,而不需要再关心底层各种文件系统的实现细节。 这里,下图是 Linux 文件系统的架构图,帮我们更好地理解系统调用、VFS、缓存、文 件系统以及块存储之间的关系。 通过这张图,可以看到,在 VFS 的下方,Linux 支持各种各样的文件系统,如 Ext4、 XFS、NFS 等等。按照存储位置的不同,这些文件系统可以分为三类。 第一类是基于磁盘的文件系统,也就是把数据直接存储在计算机本地挂载的磁盘中。常见的 Ext4、XFS、OverlayFS 等,都是这类文件系统。 第二类是基于内存的文件系统,也就是我们常说的虚拟文件系统。这类文件系统,不需要任何磁盘分配存储空间,但会占用内存。我们经常用到的 /proc 文件系统,其实就是 一种最常见的虚拟文件系统。此外,/sys 文件系统也属于这一类,主要向用户空间导出层次化的内核对象。 第三类是网络文件系统,也就是用来访问其他计算机数据的文件系统,比如 NFS、 SMB、iSCSI 等。 这些文件系统,要先挂载到 VFS 目录树中的某个子目录(称为挂载点),然后才能访问其中的文件。拿第一类,也就是基于磁盘的文件系统为例,在安装系统时,要先挂载一个根 目录(/),在根目录下再把其他文件系统(比如其他的磁盘分区、/proc 文件系统、/sys 文件系统、NFS 等)挂载进来。 文件系统 I/O把文件系统挂载到挂载点后,你就能通过挂载点,再去访问它管理的文件了。VFS 提供了一组标准的文件访问接口。这些接口以系统调用的方式,提供给应用程序使用。 就拿 cat 命令来说,它首先调用 open() ,打开一个文件;然后调用 read() ,读取文件的内容;最后再调用 write() ,把文件内容输出到控制台的标准输出中:
文件读写方式的各种差异,导致 I/O 的分类多种多样。最常见的有,缓冲与非缓冲 I/O、 直接与非直接 I/O、阻塞与非阻塞 I/O、同步与异步 I/O 等。 接下来,我们就详细看这四种分类。 第一种,根据是否利用标准库缓存,可以把文件 I/O 分为缓冲 I/O 与非缓冲 I/O。 缓冲 I/O,是指利用标准库缓存来加速文件的访问,而标准库内部再通过系统调度访问文件。 非缓冲 I/O,是指直接通过系统调用来访问文件,不再经过标准库缓存。 注意,这里所说的“缓冲”,是指标准库内部实现的缓存。比方说,你可能见到过,很多程序遇到换行时才真正输出,而换行前的内容,其实就是被标准库暂时缓存了起来。 无论缓冲 I/O 还是非缓冲 I/O,它们最终还是要经过系统调用来访问文件。我们知道,系统调用后,还会通过页缓存,来减少磁盘的 I/O 操作。 第二,根据是否利用操作系统的页缓存,可以把文件 I/O 分为直接 I/O 与非直接 I/O。 直接 I/O,是指跳过操作系统的页缓存,直接跟文件系统交互来访问文件。 非直接 I/O 正好相反,文件读写时,先要经过系统的页缓存,然后再由内核或额外的系统调用,真正写入磁盘。 想要实现直接 I/O,需要你在系统调用中,指定 O_DIRECT 标志。如果没有设置过,默认 的是非直接 I/O。 不过要注意,直接 I/O、非直接 I/O,本质上还是和文件系统交互。如果是在数据库等场景中,还会看到,跳过文件系统读写磁盘的情况,也就是我们通常所说的裸 I/O。 第三,根据应用程序是否阻塞自身运行,可以把文件 I/O 分为阻塞 I/O 和非阻塞 I/O 所谓阻塞 I/O,是指应用程序执行 I/O 操作后,如果没有获得响应,就会阻塞当前线程,自然就不能执行其他任务。 所谓非阻塞 I/O,是指应用程序执行 I/O 操作后,不会阻塞当前的线程,可以继续执行其他的任务,随后再通过轮询或者事件通知的形式,获取调用的结果。 比方说,访问管道或者网络套接字时,设置 O_NONBLOCK 标志,就表示用非阻塞方式访问;而如果不做任何设置,默认的就是阻塞访问。 第四,根据是否等待响应结果,可以把文件 I/O 分为同步和异步 I/O 所谓同步 I/O,是指应用程序执行 I/O 操作后,要一直等到整个 I/O 完成后,才能获得 I/O 响应。 所谓异步 I/O,是指应用程序执行 I/O 操作后,不用等待完成和完成后的响应,而是继续执行就可以。等到这次 I/O 完成后,响应会用事件通知的方式,告诉应用程序。 例如,在操作文件时,如果设置了 O_SYNC 或者 O_DSYNC 标志,就代表同步 I/O。如果设置了 O_DSYNC,就要等文件数据写入磁盘后,才能返回;而 O_SYNC,则是在 O_DSYNC 基础上,要求文件元数据也要写入磁盘后,才能返回。 再比如,在访问管道或者网络套接字时,设置了 O_ASYNC 选项后,相应的 I/O 就是异步I/O。这样,内核会再通过 SIGIO 或者 SIGPOLL,来通知进程文件是否可读写。 我们可能发现了,这里的好多概念也经常出现在网络编程中。比如非阻塞 I/O,通常会跟 select/poll 配合,用在网络套接字的 I/O 中。 这下我们也应该可以理解,“Linux 一切皆文件”的深刻含义。无论是普通文件和块设备、还是网络套接字和管道等,它们都通过统一的 VFS 接口来访问。 性能观测接下来,打开一个终端,SSH 登录到服务器上,我们一起来探索,如何观测文件系统的性能。 容量对文件系统来说,最常见的一个问题就是空间不足。当然,你可能本身就知道,用 df 命 令,就能查看文件系统的磁盘空间使用情况。比如:
可以看到,我的根文件系统只使用了 27% 的空间。这里还要注意,总空间用 1K- 快 的数量来表示,你可以给 df 加上 -h 选项,以获得更好的可读性:
不过有时候,明明碰到了空间不足的问题,可是用 df 查看磁盘空间后,却发现剩余空间还有很多。这是怎么回事呢? 其实除了文件数据,索引节点也占用磁盘空间。可以给 df 命令加上 -i 参数,查看索引节点的使用情况,如下所示:
索引节点的容量,(也就是 Inode 个数)是在格式化磁盘时设定好的,一般由格式化工具自动生成。当发现索引节点空间不足,但磁盘空间充足时,很可能就是过多小文件导致的。 所以,一般来说,删除这些小文件,或者把它们移动到索引节点充足的其他磁盘中,就可以解决这个问题。 缓存可以用 free 或 vmstat,来观察页缓存的大小。free 输出的 Cache,是页缓存和可回收 Slab 缓存的和,你可以从 /proc/meminfo ,直接得到它们的大小:
话说回来,文件系统中的目录项和索引节点缓存,又该如何观察呢? 实际上,内核使用 Slab 机制,管理目录项和索引节点的缓存。/proc/meminfo 只给出了 Slab 的整体大小,具体到每一种 Slab 缓存,还要查看 /proc/slabinfo 这个文件。 比如,运行下面的命令,你就可以得到,所有目录项和各种文件系统索引节点的缓存情况:
这个界面中,dentry 行表示目录项缓存,inode_cache 行,表示 VFS 索引节点缓存,其 余的则是各种文件系统的索引节点缓存。 /proc/slabinfo 的列比较多,具体含义可以查询 man slabinfo。在实际性能分析中,我们更常使用 slabtop ,来找到占用内存最多的缓存类型。 比如,下面就是运行 slabtop 得到的结果:
从这个结果你可以看到,在我的系统中,目录项(dentry)和索引节点(inode_cache)占用了最多的 Slab 缓存。不 过它们占用的内存其实并不大,加起来也只有 60MB 左右。 总结文件系统,是对存储设备上的文件,进行组织管理的一种机制。为了支持各类不同的文件系统,Linux 在各种文件系统实现上,抽象了一层虚拟文件系统(VFS)。 VFS 定义了一组所有文件系统都支持的数据结构和标准接口。这样,用户进程和内核中的其他子系统,就只需要跟 VFS 提供的统一接口进行交互。 为了降低慢速磁盘对性能的影响,文件系统又通过页缓存、目录项缓存以及索引节点缓存,缓和磁盘延迟对应用程序的影响。 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/15 9:34:02- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |