索引节点和目录项
Linux文件系统为每个文件都分配两个数据结构,索引节点(index node)和目录项(directory entry),它们主要用来记录文件的元信息和目录结构
- 索引节点(简称inode)用来记录文件的元数据,比如inode编号、文件大小、访问权限、修改日期、数据的位置等。索引节点和文件一一对应,它跟文件内容一样会被持久化存储到磁盘中。
- 目录项(简称dentry)用来记录文件的名字、索引节点指针以及与其他目录项的关联关系。多个关联的目录项,就构成了文件系统的目录结构。不同于索引节点,目录项是由内核维护的一个内存数据结构,所以通常也被叫做目录项缓存。
索引节点是每个文件的唯一标志,而目录项维护的正是文件系统的树状结构。目录项和索引节点的关系是多对一,你可以简单理解为,一个文件可以有多个别名。举个例子,通过硬链接为文件创建的别名,就会对应不同的目录项,不过这些目录项本质上还是链接同一个文件,所以,它们的索引节点相同。索引节点和目录项纪录了文件的元数据,以及文件间的目录关系,那么具体来说,文件数据到底是怎么存储的呢?是不是直接写到磁盘中就好了呢?实际上,磁盘读写的最小单位是扇区,然而扇区只有 512B 大小,如果每次都读写这么小的单位,效率一定很低。所以,文件系统又把连续的扇区组成了逻辑块,然后每次都以逻辑块为最小单元,来管理数据。常见的逻辑块大小为 4KB,也就是由连续的 8 个扇区组成。
- 目录项本身就是一个内存缓存,而索引节点则是存储在磁盘中的数据。在前面的 Buffer 和 Cache 原理中,我曾经提到过,为了协调慢速磁盘与快速 CPU 的性能差异,文件内容会缓存到页缓存 Cache 中
- 磁盘在执行文件系统格式化时,会被分成三个存储区域,超级块、索引节点区和数据块区。其中,超级块,存储整个文件系统的状态。索引节点区,用来存储索引节点。数据块区,则用来存储文件数据。
内核使用slab机制管理目录项和索引节点的缓存,可以通过/proc/slabinfo文件查看所有目录项和各种文件系统索引节点的缓存情况。其中dentry行表示目录项缓存,inode_cache行表示 VFS 索引节点缓存,其余的则是各种文件系统的索引节点缓存
[root@VM-4-5-centos lighthouse]# cat /proc/slabinfo | grep -E '^#|dentry|inode'
# name : tunables : slabdata
isofs_inode_cache 46 46 704 23 4 : tunables 0 0 0 : slabdata 2 2 0
ext4_inode_cache 4644 4644 1176 27 8 : tunables 0 0 0 : slabdata 172 172 0
jbd2_inode 128 128 64 64 1 : tunables 0 0 0 : slabdata 2 2 0
mqueue_inode_cache 16 16 1024 16 4 : tunables 0 0 0 : slabdata 1 1 0
hugetlbfs_inode_cache 24 24 680 12 2 : tunables 0 0 0 : slabdata 2 2 0
inotify_inode_mark 102 102 80 51 1 : tunables 0 0 0 : slabdata 2 2 0
sock_inode_cache 253 253 704 23 4 : tunables 0 0 0 : slabdata 11 11 0
proc_inode_cache 3168 3322 728 22 4 : tunables 0 0 0 : slabdata 151 151 0
shmem_inode_cache 882 882 768 21 4 : tunables 0 0 0 : slabdata 42 42 0
inode_cache 14688 14748 656 12 2 : tunables 0 0 0 : slabdata 1229 1229 0
dentry 27531 27531 192 21 1 : tunables 0 0 0 : slabdata 1311 1311 0
selinux_inode_security 16626 16626 40 102 1 : tunables 0 0 0 : slabdata 163 163 0
在实际性能分析中更常使用slabtop命令茶查找占用内存最多的缓存类型
[root@VM-4-5-centos lighthouse]#slabtop
Active / Total Objects (% used) : 223087 / 225417 (99.0%)
Active / Total Slabs (% used) : 7310 / 7310 (100.0%)
Active / Total Caches (% used) : 138 / 207 (66.7%)
Active / Total Size (% used) : 50536.41K / 51570.46K (98.0%)
Minimum / Average / Maximum Object : 0.01K / 0.23K / 8.00K
OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME
29295 28879 98% 0.19K 1395 21 5580K dentry
27150 27150 100% 0.13K 905 30 3620K kernfs_node_cache
26112 25802 98% 0.03K 204 128 816K kmalloc-32
23010 23010 100% 0.10K 590 39 2360K buffer_head
16626 16626 100% 0.04K 163 102 652K selinux_inode_security
14856 14764 99% 0.64K 1238 12 9904K inode_cache
9894 9894 100% 0.04K 97 102 388K ext4_extent_status
6698 6698 100% 0.23K 394 17 1576K vm_area_struct
5508 5508 100% 1.15K 204 27 6528K ext4_inode_cache
5120 5120 100% 0.02K 20 256 80K kmalloc-16
4672 4630 99% 0.06K 73 64 292K kmalloc-64
4564 4564 100% 0.57K 326 14 2608K radix_tree_node
4480 4480 100% 0.06K 70 64 280K anon_vma_chain
4096 4096 100% 0.01K 8 512 32K kmalloc-8
3570 3570 100% 0.05K 42 85 168K ftrace_event_field
3168 2967 93% 0.71K 144 22 2304K proc_inode_cache
3003 2961 98% 0.19K 143 21 572K kmalloc-192
2898 2422 83% 0.09K 63 46 252K anon_vma
2368 1944 82% 0.25K 148 16 592K filp
2080 2075 99% 0.50K 130 16 1040K kmalloc-512
2016 2016 100% 0.07K 36 56 144K Acpi-Operand
1760 1760 100% 0.12K 55 32 220K kmalloc-128
1596 1596 100% 0.09K 38 42 152K kmalloc-96
1472 1472 100% 0.09K 32 46 128K trace_event_file
虚拟文件系统
目录项、索引节点、逻辑块以及超级块,构成了 Linux 文件系统的四大基本要素。不过,为了支持各种不同的文件系统,Linux 内核在用户进程和文件系统的中间,又引入了一个抽象层,也就是虚拟文件系统 VFS(Virtual File System)。VFS 定义了一组所有文件系统都支持的数据结构和标准接口。这样,用户进程和内核中的其他子系统,只需要跟 VFS 提供的统一接口进行交互就可以了,而不需要再关心底层各种文件系统的实现细节。
磁盘顺序IO与随机IO
磁盘IO性能指标
使用率、饱和度、IOPS、吞吐量以及响应时间等。这五个指标,是衡量磁盘性能的基本指标
- 使用率,是指磁盘处理 I/O 的时间百分比。过高的使用率(比如超过 80%),通常意味着磁盘 I/O 存在性能瓶颈。
- 饱和度,是指磁盘处理 I/O 的繁忙程度。过高的饱和度,意味着磁盘存在严重的性能瓶颈。当饱和度为 100% 时,磁盘无法接受新的 I/O 请求。
- IOPS(Input/Output Per Second),是指每秒的 I/O 请求数。
- 吞吐量,是指每秒的 I/O 请求大小。
- 响应时间,是指 I/O 请求从发出到收到响应的间隔时间。
fio
我推荐用性能测试工具 fio ,来测试磁盘的 IOPS、吞吐量以及响应时间等核心指标
iostat
iostat 是最常用的磁盘 I/O 性能观测工具,它提供了每个磁盘的使用率、IOPS、吞吐量等各种常见的性能指标,当然,这些指标实际上来自 /proc/diskstats
# -d -x表示显示所有磁盘I/O的指标
$ iostat -d -x 1
Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %rrqm %wrqm r_await w_await aqu-sz rareq-sz wareq-sz svctm %util
loop0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
loop1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sda 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sdb 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
这些指标中,你要注意:
%util ,就是我们前面提到的磁盘 I/O 使用率;
r/s+ w/s ,就是 IOPS;rkB/s+wkB/s ,就是吞吐量;
r_await+w_await ,就是响应时间。
在观测指标时,也别忘了结合请求的大小( rareq-sz 和 wareq-sz)一起分析。
进程 I/O 观测
pidstat
$ pidstat -d 1
13:39:51 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
13:39:52 102 916 0.00 4.00 0.00 0 rsyslogd
从 pidstat 的输出你能看到,它可以实时查看每个进程的 I/O 情况,包括下面这些内容。
用户 ID(UID)和进程 ID(PID) 。
每秒读取的数据大小(kB_rd/s) ,单位是 KB。
每秒发出的写请求数据大小(kB_wr/s) ,单位是 KB。
每秒取消的写请求数据大小(kB_ccwr/s) ,单位是 KB。
块 I/O 延迟(iodelay),包括等待同步块 I/O 和换入块 I/O 结束的时间,单位是时钟周期。
除了可以用 pidstat 实时查看,根据 I/O 大小对进程排序,也是性能分析中一个常用的方法。这一点,我推荐另一个工具, iotop。它是一个类似于 top 的工具,你可以按照 I/O 大小对进程排序,然后找到 I/O 较大的那些进程
$ iotop
Total DISK READ : 0.00 B/s | Total DISK WRITE : 7.85 K/s
Actual DISK READ: 0.00 B/s | Actual DISK WRITE: 0.00 B/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
15055 be/3 root 0.00 B/s 7.85 K/s 0.00 % 0.00 % systemd-journald