文件系统和磁盘的区别
磁盘是一个存储设备(确切来说是块设备),可以划分为不同的磁盘分区。而在磁盘或者磁盘分区上,还可以在创建文件系统,并挂载到系统的某个目录。这样,系统就可以通过这个挂载目录,来读写文件。
换句话说,磁盘是存储数据的块设备,也是文件系统的载体。所以,文件系统确实还是要通过磁盘,来保证数据的持久化存储。
Linux 中一切皆文件。可以通过相同的文件接口,来访问磁盘和文件(比如 open、read、write、close 等)
- 通常说的“文件”,是指普通文件
- 磁盘和分区,是指块设备文件
在读写普通文件时,I/O 请求会首先经过文件系统,然后由文件系统负责,来与磁盘进行交互。而读写块设备文件时,会跳过文件系统,直接与磁盘交互,也就是所谓的“裸 I/O”。文件系统管理的缓存,是 Cache 的一部分;而裸磁盘的缓存,用的正是 Buffer。
裸磁盘,也称为原始磁盘,是一种未被任何文件系统(如NTFS、FAT32)格式化或管理的磁盘。换句话说,它是直接与磁盘硬件交互,而不通过操作系统的文件系统层进行访问。
缓存 I/O 与直接 I/O(裸磁盘 I/O )的对比
特性 | 缓存I/O(文件系统) | 直接I/O(裸磁盘) |
---|---|---|
数据流 | 磁盘 → 内核缓冲区 → 应用程序地址空间 | 磁盘 → 直接应用程序地址空间 |
缓存使用 | 使用文件系统管理的Cache | 使用磁盘的Buffer |
性能 | 适合常规文件操作,减少磁盘读写次数 | 适合高性能场景,如数据库,减少文件系统开销 |
应用场景 | 普通文件读写,系统默认方式 | 虚拟化、数据库优化、低级别磁盘操作 |
优点 | 保护系统安全,减少直接磁盘访问风险 | 降低数据复制开销,提高I/O效率 |
缺点 | 数据复制开销高,CPU和内存占用多 | 需要应用程序管理缓存,可能增加复杂性 |
Linux 文件系统如何工作
索引节点和目录项
- 索引节点(inode),和文件一一对应,存储在磁盘中,记录文件的元数据
- 目录项(dentry),记录文件的名字、索引节点以及其他目录项的关联关系
举例说明,为文件创建的硬链接,会对应不同的目录项,他们都连接到同一个文件,索引节点相同。
磁盘的最小单位是扇区,文件系统将连续的扇区组成逻辑块,以逻辑块为最小单位,来读写磁盘数据。常见的逻辑块 4KB,由连续的 8 个扇区组成。
磁盘在执行文件系统格式化时,分为三个区域:超级块、索引节点和数据块:
- 超级块:整个文件系统的状态
- 索引节点区:存储索引节点
- 数据块区:存储文件数据
虚拟文件系统
文件系统分类:
- 基于磁盘的文件系统:常见的 ext4、XFS、OverlayFS 等,都是这类文件系统
- 基于内存的文件系统:常说的虚拟文件系统,不需要磁盘空间,但是占用内存。比如,/proc 和 /sys
- 网络文件系统:用于访问其他计算机的文件系统,比如 NFS、SMB、ISCSI 等
注意:这些文件系统,要先挂载到 VFS 目录树中的某个子目录(称为挂载点),然后才能访问其中的文件。
文件系统 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 完成后,才能获得 I/O 响应
- 异步 I/O:应用程序不用等待 I/O 完成,会继续执行,等到 I/O 执行完成,会以事件的方式通知应用程序
设置 O_SYNC 或者 O_DSYNC,代表同步 I/O。如果是 O_DSYNC,要等到文件数据写入磁盘之后,才能返回,如果是 O_SYNC,是在 O_DSYNC 的基础上,要求文件元数据写入磁盘,才返回。
设置 O_ASYNC,代表异步 I/O,系统会再通过 SIGIO 或者 SIFPOLL 通知进程。
性能观测
容量
df 命令查看磁盘空间
1 | $ df -h /dev/vdb |
当索引节点空间不足,但是索引空间充足时,可能是过多小文件导致的。解决方法一般是删除这些小文件,或者移动到索引节点充足的其他磁盘区。
缓存
可以使用 free 或者 vmstat,观察页缓存的大小;也可以查看 /proc/meminfo
1 | $ cat /proc/meminfo | grep -E "SReclaimable|Cached" |
内核使用 slab 机制,管理目录项和索引节点的缓存,/proc/meminfo 给出了整体的 slab 大小,/proc/slabinfo 可以查看每一种 slab 的缓存
1 | [root@pudding-160 ~]# cat /proc/slabinfo | grep -E '^#|dentry|inode' |
其中 dentry 代表目录项缓存,inode_cache 代表 VFS 索引节点缓存,其他的就是各种文件系统的索引节点缓存。
实际性能分析中,更常使用 slabtop 命令,来找出占用内存最多的缓存类型。
示例如下:可以看到,目录项占用了最多的 Slab 缓存,大约 3.91 G
1 | # 按下c按照缓存大小排序,按下a按照活跃对象数排序 |
Linux 磁盘 I/O 工作原理
磁盘
根据存储介质,磁盘分为:
- 机械磁盘,也称为硬盘驱动器(Hard Disk Driver),通常缩写为 HDD。机械磁盘主要有盘片和读写磁头组成,数据就存储在盘片的环状磁道中。在读写数据前,需要移动读写磁头,定位数据所在的磁道,才能访问数据。如果 I/O 请求刚好连续,就不需要磁道寻址,可以获得最佳性能。这就是连续 I/O 的工作原理。与之对应的是随机 I/O,它需要不停地移动磁头,来定位数据位置,读写速度就会比较慢。
- 固态磁盘(Silid State Disk),通常缩写为 SSD,由固态电子元件组成。固态磁盘不需要磁盘寻址,不管是连续 I/O,还是随机 I/O 的性能,都比机械磁盘要好得多。
无论是机械磁盘,还是固态磁盘,相同磁盘的随机 I/O 都要比连续 I/O 慢得多,原因是:
- 随机 I/O 需要更多的磁头寻道和盘片旋转,它的性能自然要比连续 I/O 慢
- 对于固态硬盘来说,虽然它的随机性能比机械磁盘好很多,但同样存在“先擦除再写入”的限制。随机读写会导致大量的垃圾回收,所以相对应的,随机 I/O 的性能比起连续 I/O 来,也还差了很多
- 连续 I/O 还可以通过预读的方式,来减少 I/O 请求的次数,这也是其性能优异的一个原因
最小读写单位:
- 机械硬盘的最小读写单位是扇区,一般是 512 字节
- 固态硬盘的最小读写单位是页,一般是 4KB 或者 8KB
按照接口,磁盘可分为 IDE(Integrated Drive Electronics)、SCSI(Small Computer System Interface) 、SAS(Serial Attached SCSI) 、SATA(Serial ATA) 、FC(Fibre Channel)等。
磁盘介入服务器时,按照不通的使用方式,会划分为不用的架构:
- 最简单的直接作为独立磁盘设备来使用
- 将多块磁盘组合成一个逻辑磁盘,构成冗余独立磁盘阵列(RAID),提高数据访问的性能,并增强数据存储的可靠性
- 最后一种,是将磁盘组合成网络存储集群,再通过 NFS、SMB、ISCSI 等网络存储协议,暴露给服务器使用
在 Linux 中,磁盘是作为一个块设备来管理,以块为单位来读写,支持随机读写。每个块设备赋予两个设备号,分别是主、次设备号,主设备号用在驱动程序中,用来区分设备类型;次设备号用来在多个同类设备编号。