13 | 答疑(一):无法模拟出 RES 中断的问题,怎么办?
问题 1:性能工具版本太低,导致指标不全
这是使用 CentOS 的同学普遍碰到的问题。在文章中,我的 pidstat 输出里有一个 %wait 指标,代表进程等待 CPU 的时间百分比,这是 systat 11.5.5 版本才引入的新指标,旧版本没有这一项。而 CentOS 软件库里的 sysstat 版本刚好比这个低,所以没有这项指标。
不过,你也不用担心。前面我就强调过,工具只是查找分析的手段,指标才是我们重点分析的对象。如果你的 pidstat 里没有显示,自然还有其他手段能找到这个指标。
比如说,在讲解系统原理和性能工具时,我一般会介绍一些 proc 文件系统的知识,教你看懂 proc 文件系统提供的各项指标。之所以这么做,一方面,当然是为了让你更直观地理解系统的工作原理;另一方面,其实是想给你展示,性能工具上能看到的各项性能指标的原始数据来源。
这样,在实际生产环境中,即使你很可能需要运行老版本的操作系统,还没有权限安装新的软件包,你也可以查看 proc 文件系统,获取自己想要的指标。
但是,性能分析的学习,我还是建议你要用最新的性能工具来学。新工具有更全面的指标,让你更容易上手分析。这个绝对的优势,可以让你更直观地得到想要的数据,也不容易让你打退堂鼓。
当然,初学时,你最好试着去理解性能工具的原理,或者熟悉了使用方法后,再回过头重新学习原理。这样,即使是在无法安装新工具的环境中,你仍然可以从 proc 文件系统或者其他地方,获得同样的指标,进行有效的分析。
问题 2:使用 stress 命令,无法模拟 iowait 高的场景
使用 stress 无法模拟 iowait 升高,但是却看到了 sys 升高。这是因为案例中 的 stress -i 参数,它表示通过系统调用 sync() 来模拟 I/O 的问题,但这种方法实际上并不可靠。
因为 sync() 的本意是刷新内存缓冲区的数据到磁盘中,以确保同步。如果缓冲区内本来就没多少数据,那读写到磁盘中的数据也就不多,也就没法产生 I/O 压力。
这一点,在使用 SSD 磁盘的环境中尤为明显,很可能你的 iowait 总是 0,却单纯因为大量的系统调用,导致了系统 CPU 使用率 sys 升高。
这种情况,我在留言中也回复过,推荐使用 stress-ng 来代替 stress。担心你没有看到留言,所以这里我再强调一遍。
你可以运行下面的命令,来模拟 iowait 的问题。
# -i 的含义还是调用 sync,而—hdd 则表示读写临时文件
$ stress-ng -i 1 --hdd 1 --timeout 600
问题 3:无法模拟出 RES 中断的问题
这个问题是说,即使运行了大量的线程,也无法模拟出重调度中断 RES 升高的问题。
其实我在 CPU 上下文切换的案例中已经提到,重调度中断是调度器用来分散任务到不同 CPU 的机制,也就是可以唤醒空闲状态的 CPU ,来调度新任务运行,而这通常借助处理器间中断(Inter-Processor Interrupts,IPI)来实现。
所以,这个中断在单核(只有一个逻辑 CPU)的机器上当然就没有意义了,因为压根儿就不会发生重调度的情况。
不过,正如留言所说,上下文切换的问题依然存在,所以你会看到, cs(context switch)从几百增加到十几万,同时 sysbench 线程的自愿上下文切换和非自愿上下文切换也都会大幅上升,特别是非自愿上下文切换,会上升到十几万。根据非自愿上下文的含义,我们都知道,这是过多的线程在争抢 CPU。
其实这个结论也可以从另一个角度获得。比如,你可以在 pidstat 的选项中,加入 -u 和 -t 参数,输出线程的 CPU 使用情况,你会看到下面的界面:
$ pidstat -u -t 1
14:24:03 UID TGID TID %usr %system %guest %wait %CPU CPU Command
14:24:04 0 - 2472 0.99 8.91 0.00 77.23 9.90 0 |__sysbench
14:24:04 0 - 2473 0.99 8.91 0.00 68.32 9.90 0 |__sysbench
14:24:04 0 - 2474 0.99 7.92 0.00 75.25 8.91 0 |__sysbench
14:24:04 0 - 2475 2.97 6.93 0.00 70.30 9.90 0 |__sysbench
14:24:04 0 - 2476 2.97 6.93 0.00 68.32 9.90 0 |__sysbench
...
从这个 pidstat 的输出界面,你可以发现,每个 stress 线程的 %wait 高达 70%,而 CPU 使用率只有不到 10%。换句话说, stress 线程大部分时间都消耗在了等待 CPU 上,这也表明,确实是过多的线程在争抢 CPU。
在这里顺便提一下,留言中很常见的一个错误。有些同学会拿 pidstat 中的 %wait 跟 top 中的 iowait% (缩写为 wa)对比,其实这是没有意义的,因为它们是完全不相关的两个指标。
pidstat 中, %wait 表示进程等待 CPU 的时间百分比。
top 中 ,iowait% 则表示等待 I/O 的 CPU 时间百分比。
回忆一下我们学过的进程状态,你应该记得,等待 CPU 的进程已经在 CPU 的就绪队列中,处于运行状态;而等待 I/O 的进程则处于不可中断状态。
另外,不同版本的 sysbench 运行参数也不是完全一样的。比如,在案例 Ubuntu 18.04 中,运行 sysbench 的格式为:
$ sysbench --threads=10 --max-time=300 threads run
而在 Ubuntu 16.04 中,运行格式则为(感谢 Haku 留言分享的执行命令):
$ sysbench --num-threads=10 --max-time=300 --test=threads run
问题 4:无法模拟出 I/O 性能瓶颈,以及 I/O 压力过大的问题
这个问题可以看成是上一个问题的延伸,只是把 stress 命令换成了一个在容器中运行的 app 应用。
事实上,在 I/O 瓶颈案例中,除了上面这个模拟不成功的留言,还有更多留言的内容刚好相反,说的是案例 I/O 压力过大,导致自己的机器出各种问题,甚至连系统都没响应了。
之所以这样,其实还是因为每个人的机器配置不同,既包括了 CPU 和内存配置的不同,更是因为磁盘的巨大差异。比如,机械磁盘(HDD)、低端固态磁盘(SSD)与高端固态磁盘相比,性能差异可能达到数倍到数十倍。
其实,我自己所用的案例机器也只是低端的 SSD,比机械磁盘稍微好一些,但跟高端固态磁盘还是比不了的。所以,相同操作下,我的机器上刚好出现 I/O 瓶颈,但换成一台使用机械磁盘的机器,可能磁盘 I/O 就被压死了(表现为使用率长时间 100%),而换上好一些的 SSD 磁盘,可能又无法产生足够的 I/O 压力。
另外,由于我在案例中只查找了 /dev/xvd 和 /dev/sd 前缀的磁盘,而没有考虑到使用其他前缀磁盘(比如 /dev/nvme)的同学。如果你正好用的是其他前缀,你可能会碰到跟 Vicky 类似的问题,也就是 app 启动后又很快退出,变成 exited 状态。
在这里,berryfl 同学提供了一个不错的建议:可以在案例中增加一个参数指定块设备,这样有需要的同学就不用自己编译和打包案例应用了。
所以,在最新的案例中,我为 app 应用增加了三个选项。
- -d 设置要读取的磁盘,默认前缀为 /dev/sd 或者 /dev/xvd 的磁盘。
- -s 设置每次读取的数据量大小,单位为字节,默认为 67108864(也就是 64MB)。
- -c 设置每个子进程读取的次数,默认为 20 次,也就是说,读取 20*64MB 数据后,子进程退出。
你可以点击 Github 查看它的源码,使用方法我写在了这里:
$ docker run --privileged --name=app -itd feisky/app:iowait /app -d /dev/sdb -s 67108864 -c 20
案例运行后,你可以执行 docker logs 查看它的日志。正常情况下,你可以看到下面的输出:
$ docker logs app
Reading data from disk /dev/sdb with buffer size 67108864 and count 20
问题 5:性能工具(如 vmstat)输出中,第一行数据跟其他行差别巨大
这个问题主要是说,在执行 vmstat 时,第一行数据跟其他行相比较,数值相差特别大。我相信不少同学都注意到了这个现象,这里我简单解释一下。
首先还是要记住,我总强调的那句话,在碰到直观上解释不了的现象时,要第一时间去查命令手册。
比如,运行 man vmstat 命令,你可以在手册中发现下面这句话:
The first report produced gives averages since the last reboot. Additional reports give information on a sam‐
pling period of length delay. The process and memory reports are instantaneous in either case.
也就是说,第一行数据是系统启动以来的平均值,其他行才是你在运行 vmstat 命令时,设置的间隔时间的平均值。另外,进程和内存的报告内容都是即时数值。
你看,这并不是什么不得了的事故,但如果我们不清楚这一点,很可能卡住我们的思维,阻止我们进一步的分析。这里我也不得不提一下,文档的重要作用。
授之以鱼,不如授之以渔。我们专栏的学习核心,一定是教会你性能分析的原理和思路,性能工具只是我们的路径和手段。所以,在提到各种性能工具时,我并没有详细解释每个工具的各种命令行选项的作用,一方面是因为你很容易通过文档查到这些,另一方面就是不同版本、不同系统中,个别选项的含义可能并不相同。
所以,不管因为哪个因素,自己 man 一下,一定是最快速并且最准确的方式。特别是,当你发现某些工具的输出不符合常识时,一定记住,第一时间查文档弄明白。实在读不懂文档的话,再上网去搜,或者在专栏里向我提问。
学习是一个“从薄到厚再变薄”的过程,我们从细节知识入手开始学习,积累到一定程度,需要整理成一个体系来记忆,这其中还要不断地对这个体系进行细节修补。有疑问、有反思才可以达到最佳的学习效果。