基本概念
逻辑io和物理io的区别
逻辑io:由应用程序发给文件系统的io;
物理io:由文件系统发给磁盘的io,或者裸io(应用程序直接发给磁盘的io)
随机io和连续io的区别
随机io文件偏移量的位置是随机的;连续io文件偏移量的位置是相连的,提高读写性能;
直接io及目的
应用程序绕过缓存使用文件系统,类似同步写。
直接io可以用于备份文件系统的应用程序,防止只读一次的数据污染文件系统缓存。直接io也可以用于在进程堆中自建缓存的应用,避免双重缓存的问题。
非阻塞io
在读写时返回错误代码,让应用程序过一会再重试,而不是让线程阻塞。
VFS职责
虚拟文件系统给不同的文件系统提供了一个通用接口。
文件系统延时
是一个文件系统逻辑请求从开始到结束的时间,包括消耗在文件系统、内核磁盘io子系统和等待磁盘设备的时间。
预取(预读)的目的
在大量的文件顺序读io时,例如文件系统备份,这种数据量可能太大了,放不进缓存,或者只读一次在缓存留不住,造成缓存命中偏低,系统性能较差。
预取是解决这类问题的通常做法。具体是:检查当前和上一个io的文件偏移量,可以检测出当前是否是顺序读负载,并做出预测,在应用程序请求前向磁盘发出读命令,以填充文件系统缓存。当应用程序真的发出读请求时,就会命中缓存,从而提高读效率。
fsync()相对O_SYNC的优势
O_SYNC是单次同步写,fsync()是一组已写io的同步提交,同步提交之前异步写入数据,通过合并同步写提高性能。
mmap()相对read()/write()的优势和劣势
mmap内存文件映射,通过把文件映射到进程地址空间,并直接存取内存地址的方法来提高文件系统io性能。避免了read()/write()操作产生的系统调用和上下文切换。
但在多处理器系统上,映射文件的缺点在于同步每个CPU MMU的开销,尤其是跨CPU的映射删除调用。
逻辑io变成物理io时放大的原因
- 文件系统元数据,增加额外的io;
- 文件系统记录尺寸,向上对齐的io大小(增加了字节),或者被打散的io(增加了io数量)
- 卷管理器奇偶数校验,读-改-写的周期会增加额外的io
逻辑io变成物理io时缩小的原因
- 文件系统缓存,直接从主存返回,不经过磁盘;
- 文件系统写抵消,在一次性写回磁盘之前,同一个地方被多次修改;
- 压缩
- 归并,合并连续io
- 内存文件系统,不需要写到磁盘
文件系统写时复制如何提高性能
写时复制步骤:
- 写时把旧数据的拷贝并更新放到新块;
- 更新引用指向新块;
- 把老块放到空闲链表。
写时复制可以把随机写变成连续写,改善写入性能。
分析工具
strace
可以测试文件系统延迟,包括系统调用接口的调试。该调试器会影响性能。
# strace -ttT -p 643
strace: Process 643 attached
04:23:07.342886 restart_syscall(<... resuming interrupted poll ...>) = 0 <0.044331>
04:23:07.387434 times({tms_utime=865447, tms_stime=736256, tms_cutime=0, tms_cstime=0}) = 1879933271 <0.000068>
.........
04:23:07.790606 poll([{fd=4, events=POLLIN}, {fd=5, events=POLLIN}], 2, 26) = 0 (Timeout) <0.026162>
04:23:07.816858 times({tms_utime=865447, tms_stime=736256, tms_cutime=0, tms_cstime=0}) = 1879933314 <0.000061>
04:23:07.816990 open("/proc/meminfo", O_RDONLY) = 7 <0.000070>
04:23:07.817166 fstat(7, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0 <0.000040>
04:23:07.817309 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1d1d984000 <0.000047>
04:23:07.817397 read(7, "MemTotal: 999936 kB\nMemF"..., 1024) = 1024 <0.000090>
04:23:07.817543 read(7, "Total: 0\nHugePages_Free: "..., 1024) = 174 <0.000010>
04:23:07.817626 read(7, "", 1024) = 0 <0.000008>
04:23:07.817660 close(7) = 0 <0.000035>
-tt在左侧打印相对时间戳,-T在右侧打印系统调用时间,-p 643是进程号。
下面这个read大小为1KB,花费90us,在文件描述符7完成,如果这个读是在文件系统的读(而不是套接字的读),之前的输出会有open()系统调用。
04:23:07.817397 read(7, "MemTotal: 999936 kB\nMemF"..., 1024) = 1024 <0.000090>
DTrace&SystemTap
DTrace能从系统调用接口、VFS接口,或文件系统内部的角度来检查文件系统行为。
SystemTap功能类似。
free/top/vmstat
# free
total used free shared buff/cache available
Mem: 999936 145880 123912 26220 730144 598012
Swap: 2097148 107912 1989236
buff/cache展示缓冲区高速缓存和页缓存的大小
top命令也类似
# top
top - 20:05:50 up 168 days, 12:47, 2 users, load average: 0.00, 0.01, 0.05
Tasks: 106 total, 1 running, 105 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.4 us, 0.2 sy, 0.0 ni, 99.4 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 999936 total, 123648 free, 146124 used, 730164 buff/cache
KiB Swap: 2097148 total, 1989236 free, 107912 used. 597768 avail Mem
vmstat也分别展示了buff缓冲区高速缓存和cache页缓存的大小
# vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 0 107912 124236 0 730164 0 0 2 2 0 2 0 0 99 0 0
sar
$ sar -vr 1
Linux 3.10.0-957.1.3.el7.x86_64 (k8snode-4) 02/22/2019 _x86_64_ (8 CPU)
09:24:29 AM kbmemfree kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty
09:24:30 AM 213576 16217432 98.70 20 11430948 8656860 52.69 7568936 6243672 100
09:24:29 AM dentunusd file-nr inode-nr pty-nr
09:24:30 AM 887656 7968 272848 2
-v打印下面信息
dentunusd:目录项缓存未用计数(可用项)
file-nr:使用中的文件描述符个数
inode-nr:使用中的inode个数
-r打印的消息
kbbuffers:缓冲区高速缓存大小 (KB)
kbcached:页缓存大小(KB)
其他工具
df:报告文件系统使用情况和容量统计
mount:显示文件系统挂载选项
inotify:linux文件系统事件监控