如何在Linux上对交换性和启动时间进行排查

136 阅读7分钟

我最近在Linux的启动顺序中又遇到了一个有趣的问题,这个问题有一个规避的办法--不是一个解决方案。它的开始很意外。

我正在写几篇文章,同时对我的系列书籍《使用和管理Linux》的个人副本做一些更新。从零到系统管理员"。我打开了四个LibreOffice Write的实例来做这些事情。我有三个用VirtualBox运行的虚拟机来测试我正在写的一些东西。我还打开了LibreOffice Impress来做一个无关的演示。我喜欢听音乐,所以我在火狐浏览器中打开了几个标签中的一个,即潘多拉,我所选择的音乐流媒体服务。我用Konsole打开了多个Bash shells,其中有许多标签,还有一个是Alpine文本模式的电子邮件客户端。然后还有Thunar文件管理器中的各种标签。

所以我有很多事情要做。就像我现在写这篇文章时一样。

症状

当我使用这些开放的会话时,我注意到在等待系统将文件写入M.3固态硬盘时,速度明显变慢了--这个过程本来应该非常快。我还注意到,音乐是不稳定的,每隔几分钟就完全消失。总体来说,性能很差。我开始认为Fedora有一个严重的问题。

我的主要工作站,也就是我当时工作的那台,有64GB的内存和英特尔Core i9 Extreme,有16个核心和超线程(32个CPU),使用我配置的超频可以运行到4.1GHz。因此,我不应该遇到任何减速--或者我当时是这么想的。

确定问题

找到问题的时间并不长,因为我以前在内存少得多的系统上也遇到过类似的症状。这个问题看起来像是由于页面交换造成的延迟。但为什么呢?

我开始使用我确定问题的常用工具之一,HTTP。它显示,系统使用了13.6GB的内存用于程序,其余的大部分内存都用于缓存和缓冲区。它还显示,交换正在积极发生,大约253MB的数据被存储在交换分区中。

Date & Time: 2022-08-12 10:53:08
Uptime: 2 days, 23:47:15
Tasks: 200, 1559 thr, 371 kthr; 4 running
Load average: 3.97 3.05 2.08
   
Disk IO: 202.6% read: 687M write: 188K
Network: rx: 0KiB/s tx: 0KiB/s (0/0 packets)
Systemd: running (0/662 failed) (0/7912 jobs)     
Mem[|||||||##*@@@@@@@@@@@@@@@@@@@@@@@@@@    13.6G/62.5G]
Swp[||#                                      253M/18.0G]

但这意味着我还有很多内存,系统可以直接用于程序和数据,还有更多内存可以从缓存和缓冲区恢复。那么,这个系统为什么要进行交换呢?

我听说过 "交换性 "的因素。但那是很久以前的事了。我做了一些关于 "交换性 "的搜索,以了解内核设置vm.swappiness

这个内核参数的默认值是60。这代表了尚未使用的自由内存的百分比。当系统达到60%的触发点时,它就开始交换,不管有多少可用的内存。我的系统在剩下大约0.6*62.5GB=37.5GB的未使用内存时开始交换。

根据我的在线阅读,我发现对于许多Linux系统来说,10%是一个更好的设置。有了这个设置,当只有10%的内存是空闲的时候,交换就开始了。

我检查了我系统上当前的交换性设置,它被设置为默认值。

# sysctl vm.swappiness
vm.swappiness = 60

是时候改变这个内核设置了。

修复问题

我不会深入研究血淋淋的细节,但底线是,以下任何一个命令,以root身份运行,将立即在运行中的Linux电脑上完成工作,而无需重启。

# sysctl -w vm.swappiness=10

你也可以使用下面这个命令来做同样的事情。

# echo 10 > /proc/vm/swappiness

Tecmint有一篇关于设置内核参数的优秀文章。

这两个命令都改变了/proc 文件系统中的实时内核设置。在运行这两个命令中的任何一个后,你应该运行sysctl vm.swappiness 命令来验证内核设置是否已经改变。

但是这些命令只改变了当前运行的系统的交换率值。重启后,该值将恢复到默认值。我需要确保这一改变在重启后是持久的。

但首先,失败

为了永久改变内核vm.swappiness变量,我使用了我之前的文章《[我如何在Linux上禁用IPv6]》中描述的程序,在/etc/default/grub 文件的末尾添加了以下一行。

GRUB_CMDLINE_LINUX="vm.swappiness=1"

然后我以root身份运行grub2-mkconfig 命令,重建/boot/grub2/grub.cfg 文件。然而,用虚拟机和真正的硬件进行的测试表明,这并不奏效,swappiness值也没有变化。所以我尝试了另一种方法。

而成功

在这个启动时的故障和我在《我如何在Linux上禁用IPv6》一文中描述的故障,以及我因遇到这两个问题而探索的其他启动问题之间,我决定这是一个Linux启动时的问题。换句话说,一些所需的服务,其中之一可能是网络本身,没有启动和运行,这使得这些内核选项的变化无法提交到/proc 文件系统中,或者它们被提交后在服务启动时被覆盖。

我可以通过将它们添加到一个新的文件,/etc/sysctl.d/local-sysctl.conf ,其中包括我所有的本地内核选项修改,使所有这些都能正常工作。

###############################################
#            local-sysctl.conf                #
#                                             #
# Local kernel option settings.               #
# Install this file in the /etc/sysctl.d      #
# directory.                                  #
#                                             #
# Use the command:                            #
# sysctl -p /etc/sysctl.d/local-sysctl.conf   #
# to activate.                                #
#                                             #
###############################################
###############################################
# Local Network settings                      #
# Specifically to disable IPV6                #
###############################################
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1

###############################################
# Virtual Memory                              #
###############################################
# Set swappiness
vm.swappiness = 1

然后我运行下面的命令,它只激活了指定文件中的内核选项。

# sysctl -p /etc/sysctl.d/local-sysctl.conf
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
vm.swappiness = 13

这是一个比我在关于禁用IPv6的文章中使用的更有针对性的设置内核选项的方法。

报告该错误

在写这篇文章的时候,还没有真正解决这个问题的根本原因--不管是什么原因。有一种方法可以暂时规避这个问题,直到提供一个修复方法。我使用了为测试而创建的/etc/sysctl.d/local-sysctl.conf 文件,并添加了一个 systemd 服务,在启动顺序的最后运行,等待几秒钟,然后在这个新文件上运行sysctl 。如何做到这一点的细节在《我如何在Linux上禁用IPv6》一文中。

在尝试禁用 IPv6 时,我已经使用 Red Hat 的 Bugzilla 报告了这个 bug 2103517。我把这个新的信息添加到该bug中,以确保我的最新发现可以提供给内核开发者。

最后的想法

经过实验看我能多好地重现这些症状,以及许多其他的症状,我已经确定vm.swappiness设置为60%对于许多大内存的Linux系统来说是太激进了。由于没有比我自己的电脑更多的数据点,我只能初步断定,拥有大量内存但不经常使用的系统是这个问题的主要受害者。

解决本地内核选项设置不起作用问题的直接办法是在启动后设置。我实现的自动化是一个很好的例子,说明如何使用systemd来取代旧的SystemV启动文件rc.local

这个bug之前没有被报道过。经过几天的实验,我发现本地设置的内核选项在启动时没有被设置或保留,这个问题很容易在多个物理和虚拟系统上重复出现。在这一点上,我觉得必须报告这个错误,以确保它被修复。报告它是我回馈Linux社区的另一种方式。