这篇文章上次修改于 899 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
1 索引节点和目录项
索引节点,简称 inode,记录文件的元数据,如 inode 编号,文件大小,修改日期,访问权限,数据位置等。索引节点会占用磁盘空间。索引节点是文件的唯一标志。
目录项,简称 dentry,记录文件的目录,索引节点指针和与其它目录项的关联关系。是一个内存缓存,不占磁盘空间。
磁盘在执行文件系统格式化时,会被分成三个存储区:
- 超级块:存储整个文件系统的状态。
- 索引节点区:存储索引节点。
- 数据块区:存储文件数据。
2 虚拟文件系统
Linux 文件系统四大要素:目录项,索引节点,逻辑块,超级块。
逻辑块:文件系统将连续的扇区组成了逻辑块,然后以逻辑块为最小单元管理数据。常见的逻辑块大小为 4KB,即由连续的 8 个扇区组成。
VFS 定义了一组所有文件系统都支持的数据结构和标准接口。
按照存储位置的不同,文件系统可分为三类:
- 基于磁盘的文件系统,是直接挂载在磁盘中,如 Ext4 等。
- 基于内存的文件系统,即虚拟文件系统,如 /proc,/sys 等。
- 网络文件系统,如 NFS 等。
这些文件系统需要先挂载在 VFS 目录树中的某个子目录(挂载点),然后才能访问其中的文件。
3 文件系统 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 操作后,不用等待完成后的响应,可继续执行任务,随后通过轮询或事件通知的形式获取调用结果。比如访问网络套接字时,需指定 O_NONBLOCK 标志。
根据是否等待响应结果,分为:
- 同步 I/O:程序执行 I/O 后,需要等整个 I/O 操作执行完后,才能获得响应。操作文件时,如果设置 O_SYNC,等文件写入磁盘后,才能返回;如果设置了 O_DSYNC,还需要等文件元数据也写入磁盘后才能返回。
- 异步 I/O:程序执行 I/O 后,不需要等待响应,继续执行任务。等本次 I/O 结束后,响应会用通知的方式告知应用程序。访问网络时,设置了 O_ASYNC 后,内核会通过 SINGO 或者 SIGPOLL 通知进程文件是否可读写。
4 性能观测
4.1 容量
查看文件系统容量:
$ df -h /dev/sda1
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 29G 3.1G 26G 11% /
文件系统使用了 11% 的空间。
查看索引节点使用情况:
$ df -i /dev/sda1
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 3870720 157460 3713260 5% /
当发现索引节点空间不足,但磁盘空间比较足时,可能是过多的小文件导致的。
4.2 缓存
可通过 free 或 vmstat 观察页缓存的大小。free 输出的 Cache 是页缓存和可回收 Slab 之和。
$ cat /proc/meminfo | grep -E "SReclaimable|Cached"
Cached: 748316 kB
SwapCached: 0 kB
SReclaimable: 179508 kB
内核使用 Slab 机制管理目录项和索引节点缓存。查看目录项和索引节点缓存:
$ cat /proc/slabinfo | grep -E '^#|dentry|inode'
# name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
xfs_inode 0 0 960 17 4 : tunables 0 0 0 : slabdata 0 0 0
...
ext4_inode_cache 32104 34590 1088 15 4 : tunables 0 0 0 : slabdata 2306 2306 0hugetlbfs_inode_cache 13 13 624 13 2 : tunables 0 0 0 : slabdata 1 1 0
sock_inode_cache 1190 1242 704 23 4 : tunables 0 0 0 : slabdata 54 54 0
shmem_inode_cache 1622 2139 712 23 4 : tunables 0 0 0 : slabdata 93 93 0
proc_inode_cache 3560 4080 680 12 2 : tunables 0 0 0 : slabdata 340 340 0
inode_cache 25172 25818 608 13 2 : tunables 0 0 0 : slabdata 1986 1986 0
dentry 76050 121296 192 21 1 : tunables 0 0 0 : slabdata 5776 5776 0
- dentry:目录项缓存。
- inode_cache:VFS 索引节点缓存。
- 其余是各种文件系统索引节点缓存。
查看占用内存最多的缓存类型:
# 按下c按照缓存大小排序,按下a按照活跃对象数排序
$ slabtop
Active / Total Objects (% used) : 277970 / 358914 (77.4%)
Active / Total Slabs (% used) : 12414 / 12414 (100.0%)
Active / Total Caches (% used) : 83 / 135 (61.5%)
Active / Total Size (% used) : 57816.88K / 73307.70K (78.9%)
Minimum / Average / Maximum Object : 0.01K / 0.20K / 22.88K
OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME
69804 23094 0% 0.19K 3324 21 13296K dentry
16380 15854 0% 0.59K 1260 13 10080K inode_cache
58260 55397 0% 0.13K 1942 30 7768K kernfs_node_cache
485 413 0% 5.69K 97 5 3104K task_struct
1472 1397 0% 2.00K 92 16 2944K kmalloc-2048
可见,目录项和索引节点占用了最多的 Slab 缓存,但加起来只有 23MB 左右。
参考
倪朋飞. Linux 性能优化实战.
没有评论