前面几章我们介绍了文件系统和磁盘的相关信息,其中大篇幅讲了I/O请求落到磁盘的整个过程,以及可能存在性能瓶颈的地方和排查工具方法。还未看过的,可以到文章末尾参阅之前的文章。
今天主要来介绍工具strace,并由它来组合lsof、vmstat、iostat、pidstat等工具共同分析文件系统存在的性能问题。
为什么要用这个工具?一是它安装使用比较方便、容易上手,可以通过 apt install strace 命令来安装,安装后直接通过strace命令使用,无需额外配置。再就是他们主要跟文件操作有关,我们在看磁盘I/O请求的过程时,一般出现问题最多的不是底层块设备层,反而是虚拟文件系统层,因为它面对的是各种各样的应用程序,绝大部分的I/O请求也是来源于此。
1、VFS的核心操作
虚拟文件系统(VFS)的核心操作主要包含 open、read、write、close、stat、fsync
等,它们比较集中且统一,有利于我们分析。而且通过分析这些系统调用的耗时和调用次数,我们也可以很快定位文件系统性能瓶颈。
open/close:表示打开/关闭文件,可能存在的性能问题集中在频繁打开、打开延迟等方面。
read/write:表示读写文件数据,关注性能指标集中在IOPS、吞吐量、缓冲区大小等上面。
stat/lstat/fstat:通过这几个操作可以获取文件的状态。
fsync:刷新磁盘,更新数据。可以关注一下刷新磁盘是否延迟。
除了这几个比较常用的,还有mmap内存映射和access的权限检查等,在必要时可以配合使用。
2、性能分析四步
在使用strace工具分析文件系统调用情况之前,我们需要使用一些前置工具来对系统进行分析一下是否调用这个工具来分析,才能达到更好的效果。
第一步
一般在出现问题时,我们首先要使用的工具是free,top等,查看一下系统整体运行情况。
例如,运行top -c命令
bash
$ top - c
11:19:22 up 790 days, 18:52, 4 users, load average: 0.00, 0.00, 0.00
Tasks: 136 total, 1 running, 87 sleeping, 0 stopped, 0 zombie
%Cpu(s): 6.8 us, 3.3 sy, 0.0 ni, 89.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 2041048 total, 428584 free, 246764 used, 1365700 buff/cache
KiB Swap: 14401532 total, 14029564 free, 371968 used. 1523972 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
45479 root 20 0 1246604 17568 8288 S 4.7 0.9 5362:53 /usr/bin/telegraf --config /etc/telegraf/telegraf.conf
27426 root 20 0 41828 3548 2972 R 0.7 0.2 0:00.04 top -c
16 root 20 0 0 0 0 S 0.3 0.0 146:37.59 [ksoftirqd/1]
3248 redis 20 0 53376 2088 1652 S 0.3 0.1 813:41.59 /usr/bin/redis-server 127.0.0.1:6379
1 root 20 0 119712 4544 3000 S 0.0 0.2 190:35.95 /sbin/init
大体可以判断以下几个关键信息
- 基本负载信息,load average: 0.00, 0.00, 0.00。
- 内核态 CPU 使用率,因为他们会反映出系统调用、进程调度等操作,指标中的3.3 sy。
- CPU 等待 IO 完成的时间占比,也就是iowait,指标中的0.0 wa。
- CPU 空闲率(越高说明 CPU 越空闲),指标中的89.8 id。
如果存在I/O问题,一般情况下iowait会表现出来,数据持续超过10%,说明I/O压力比较大。如果sy的值持续超过20%,说明内核调度频繁或系统调用过多,需要进一步排查了。
第二步
若在第一步观察中,发现iowait确实挺高,说明这是一个潜在的性能瓶颈。我们再用iostat工具,看一下哪个磁盘I/O的使用率(%util)比较高,是否存在饱和度问题(队列avgqu-
sz)。
bash
root@node:~# iostat -x -d 1
Linux 4.15.0-58-generic (cs1ahyper01n07) 11/17/2025 _x86_64_ (64 CPU)
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.00 7.94 0.03 14.19 0.88 98.64 14.00 0.00 0.07 0.37 0.07 0.01 0.01
有了这些基本信息后,我们再进一步排查。若磁盘使用率持续比较高,我们需要找出引起它升高的进程。
第三步
排查到这一步,可以用 pidstat 或者 iotop ,进一步观察进程的 I/O 情况。
bash
root@node:~# pidstat -d 1
Linux 4.15.0-58-generic (cs1ahyper01n07) 11/17/2025 _x86_64_ (64 CPU)
11:41:10 AM UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
11:41:11 AM 0 4855 0.00 29.63 0.00 0 bkunifylogbeat
11:41:11 AM 0 4893 0.00 3.70 0.00 0 exceptionbeat
11:41:11 AM 0 24244 0.00 7.41 0.00 0 qemu-system-x86
11:41:11 AM 0 38781 0.00 3.70 0.00 0 qemu-system-x86
11:41:11 AM 0 48974 0.00 459.26 0.00 0 qemu-system-x86
根据输出,我们可以很快找出kB_rd/s、kB_wr/s两个指标比较大的进程,以及这个进程的ID号。而且根据这个进程ID还可以找出所有线程ID和相关信息。
bash
root@node:~# ps -efT |grep 3565
root 3565 3565 14596 0 Mar04 ? 00:00:00 /usr/bin/telegraf --config /etc/telegraf/telegraf.conf
root 3565 3566 14596 0 Mar04 ? 02:01:44 /usr/bin/telegraf --config /etc/telegraf/telegraf.conf
root 3565 3567 14596 0 Mar04 ? 08:45:51 /usr/bin/telegraf --config /etc/telegraf/telegraf.conf
root 3565 3568 14596 0 Mar04 ? 08:25:29 /usr/bin/telegraf --config /etc/telegraf/telegraf.conf
root 3565 3569 14596 0 Mar04 ? 07:48:13 /usr/bin/telegraf --config /etc/telegraf/telegraf.conf
root 3565 3570 14596 0 Mar04 ? 00:00:00 /usr/bin/telegraf --config /etc/telegraf/telegraf.conf
第四步
根据上面三步我们找到的具体进程,我们就可以使用strace来具体分析它的文件调用情况了。
strace的基本用法:
例如,跟踪一个进程的所有文件操作:
bash
strace -e trace=file -tt -T -p <PID>
其中:
-e trace=file 只跟踪与文件操作相关的系统调用(包括文件路径作为参数的系统调用)
-tt 打印时间戳(包括微秒)
-T 选项来查看每个系统调用的耗时,然后找出耗时较长的调用
-p 指定要跟踪的进程ID
其他参数:
-c 选项来统计系统调用的次数、错误和耗时
-o 输出到文件中
-f 跟踪子进程
-s 1024 显示字符串的最大长度
例如:
bash
root@node1:~# strace -e trace=stat -p 3442
strace: Process 3442 attached
--- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=3447, si_uid=114} ---
stat("promote", 0x7ffd312ccf10) = -1 ENOENT (No such file or directory)
stat("fallback_promote", 0x7ffd312ccf10) = -1 ENOENT (No such file or directory)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=42508, si_uid=114, si_status=0, si_utime=0, si_stime=0} ---
--- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=3447, si_uid=114} ---
根据第三步中查到的进程ID,我们使用strace -p 来查看进程的文件调用情况。
bash
root@node:~# strace -e trace=read,write -p 60274
strace: Process 60274 attached
read(5, "\1\0\0\0\0\0\0\0", 512) = 8
write(20, "\1\0\0\0\0\0\0\0", 8) = 8
read(5, "\1\0\0\0\0\0\0\0", 512) = 8
write(20, "\1\0\0\0\0\0\0\0", 8) = 8
write(9, "\1\0\0\0\0\0\0\0", 8) = 8
write(9, "\1\0\0\0\0\0\0\0", 8) = 8
write(9, "\1\0\0\0\0\0\0\0", 8) = 8
read(9, "\3\0\0\0\0\0\0\0", 16) = 8
read(9, 0x7ffe9b2b8060, 16) = -1 EAGAIN (Resource temporarily unavailable)
有时候read()、write()调用中只能看到文件的描述符编号,文件名和路径还是未知的。这时候我们还需要借助lsof,它专门用来查看进程打开文件的列表,不过,这里的"文件"不只有普通文件,还包括了目录、块设备、动态库、网络套接字等。
bash
root@node:~# lsof -p 60274
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
qemu-syst 60274 root cwd DIR 8,1 4096 2 /
qemu-syst 60274 root rtd DIR 8,1 4096 2 /
qemu-syst 60274 root txt REG 8,1 22519968 1587004 /usr/bin/qemu-system-x86_64
其中,FD 表示文件描述符号,TYPE 表示文件类型,NAME 表示文件路径。
根据查到的具体文件、应用等信息就可以进一步分析具体问题了。