KSM(Kernel Samepage Merging)是Linux内核自带的一项内存优化技术,它能扫描系统中内容相同的内存页并将其合并为一页,从而释放大量物理内存。在CentOS系统中,ksmtuned是专门用来动态管理和调节KSM行为的守护进程服务。简单说,如果你的服务器跑了大量虚拟机或者容器,内存吃紧,那么开启并合理调节ksmtuned就是最直接有效的手段。核心操作就三步:安装ksmtuned、配置参数、启动服务并监控效果。下面我把每个环节拆开讲透。

一、KSM和ksmtuned到底是什么关系

KSM是内核层面的功能,它通过扫描/sys/kernel/mm/ksm/目录下的参数来工作。但内核自己不会智能调节,它需要一个用户态的工具来告诉它"什么时候该扫描、扫描多快、合并多少页"。ksmtuned就是这个角色——它是一个基于Python的守护进程,会根据系统当前的内存使用情况自动调整KSM的运行参数。没有ksmtuned,KSM虽然能用,但你得手动去改参数,效率低且容易出问题。有了ksmtuned,系统会自己判断内存压力,动态调节扫描频率和合并阈值。

在CentOS 7和CentOS 8/Stream中,ksmtuned都可以通过yum或dnf直接安装。它依赖qemu-kvm包(因为KSM最初就是为KVM虚拟化设计的),但即使你不跑虚拟机,只要系统内存有重复页,KSM同样能起作用。

二、安装ksmtuned的具体步骤

首先确认系统是否已经安装了ksmtuned:

rpm -qa | grep ksmtuned

如果没有输出,执行安装:

yum install -y ksmtuned

CentOS 8/Stream用户用dnf:

dnf install -y ksmtuned

安装完成后,检查ksmtuned的配置文件位置:

ls -la /etc/ksmtuned.conf

这个文件就是后续所有调优的核心。默认配置已经能满足大部分场景,但如果你想精细控制,就需要深入改参数。

三、ksmtuned.conf核心参数详解

打开配置文件:

vi /etc/ksmtuned.conf

文件内容大致如下,我逐个解释每个关键参数:

[ksmtuned]
# 内存空闲阈值,低于此值时ksmtuned会停止合并
# 单位是MB,默认512MB
free_mb=512

# 内存使用阈值,高于此值时ksmtuned开始积极工作
# 单位是MB,默认1024MB
threshold_mb=1024

# 扫描间隔,单位毫秒,默认20ms
scan_interval_ms=20

# 每次扫描的最大页数,默认1000
max_scan_pages=1000

# 睡眠时间,单位毫秒,默认10ms
sleep_ms=10

# 合并跨NUMA节点的页面,默认1(开启)
merge_across_nodes=1

free_mb:这是最重要的参数之一。当系统空闲内存低于这个值时,ksmtuned会认为内存已经够紧张了,不应该再花CPU去扫描合并,否则会影响业务性能。如果你的服务器内存总量小(比如8GB),建议设为256或384;如果是64GB以上的大内存机器,可以设为1024甚至更高。

threshold_mb:当系统已使用内存超过这个值时,ksmtuned会加大扫描力度。这个值应该设得比free_mb高,形成一个"工作区间"。比如free_mb=512,threshold_mb=2048,意味着内存使用在512MB到2048MB之间时ksmtuned处于正常工作状态。

scan_interval_ms:扫描间隔越小,KSM工作越频繁,CPU占用越高,但内存回收越快。默认值20ms对大多数场景合适。如果你发现CPU被ksmtuned吃掉太多(超过10%),可以调大到50甚至100。反之如果内存回收不够快,可以降到10。

max_scan_pages:每次扫描的最大页数。这个值越大,单次扫描处理的页面越多,效率高但CPU峰值也高。默认1000是个平衡点。对于内存特别大的机器(128GB+),可以适当提高到2000或5000。

merge_across_nodes:NUMA架构的服务器有多个内存节点,跨节点合并会增加延迟但能回收更多内存。如果你的业务对延迟敏感(比如数据库),设为0关闭跨节点合并;如果是虚拟化宿主机追求最大内存利用率,保持1。

四、启动服务并设置开机自启

配置改完后,启动ksmtuned:

systemctl start ksmtuned
systemctl enable ksmtuned

验证服务状态:

systemctl status ksmtuned

你应该看到active (running)的状态。如果启动失败,检查日志:

journalctl -u ksmtuned -f

常见失败原因是/sys/kernel/mm/ksm/目录下的参数无法写入,通常是因为内核没有编译KSM支持或者SELinux阻止。确认内核支持:

cat /sys/kernel/mm/ksm/run

如果输出1,说明KSM已在内核中启用。如果是0,需要手动开启:

echo 1 > /sys/kernel/mm/ksm/run

但注意这只是临时生效,重启后会恢复。要永久开启,在/etc/rc.local或创建systemd服务来执行这个命令。

五、手动调节KSM参数的进阶操作

有时候你不想用ksmtuned自动管理,而是想自己精确控制KSM。可以直接操作/sys/kernel/mm/ksm/下的参数:

# 开启KSM
echo 1 > /sys/kernel/mm/ksm/run

# 设置扫描页数(每次扫描多少页)
echo 1000 > /sys/kernel/mm/ksm/pages_to_scan

# 设置睡眠时间(扫描完后休眠多久,单位毫秒)
echo 10 > /sys/kernel/mm/ksm/sleep_millisecs

# 查看当前合并了多少页
cat /sys/kernel/mm/ksm/pages_shared

# 查看节省了多少内存(单位KB)
cat /sys/kernel/mm/ksm/pages_saving

pages_sharing是已经被合并的页面数,pages_saving是节省的内存量(因为每合并一页就少用一页内存)。这两个数字是衡量KSM效果的直接指标。如果pages_saving持续增长,说明KSM在正常工作。

一个实用的监控脚本,可以定期记录KSM状态:

#!/bin/bash
echo "=== KSM Status $(date) ===" >> /var/log/ksm_status.log
echo "run: $(cat /sys/kernel/mm/ksm/run)" >> /var/log/ksm_status.log
echo "pages_shared: $(cat /sys/kernel/mm/ksm/pages_shared)" >> /var/log/ksm_status.log
echo "pages_saving: $(cat /sys/kernel/mm/ksm/pages_saving)" >> /var/log/ksm_status.log
echo "pages_scanned: $(cat /sys/kernel/mm/ksm/pages_scanned)" >> /var/log/ksm_status.log
echo "full_scans: $(cat /sys/kernel/mm/ksm/full_scans)" >> /var/log/ksm_status.log
echo "" >> /var/log/ksm_status.log

把这个脚本放到crontab每5分钟执行一次,就能持续追踪KSM的工作状态。

六、不同场景下的调优建议

场景一:KVM虚拟化宿主机。这是KSM最经典的应用场景。多个虚拟机运行相同的操作系统时,内核代码页、库文件页大量重复。建议free_mb设为1024,threshold_mb设为4096,scan_interval_ms设为20。如果宿主机CPU核心数多(32核以上),max_scan_pages可以设到5000。实际测试中,一台64GB内存的KVM宿主机跑8个CentOS虚拟机,开启KSM后可节省3-8GB内存。

场景二:容器密集部署(Docker/Podman)。容器共享宿主机内核,镜像层的页面重复率高。但容器场景下要注意,ksmtuned默认可能不够积极。建议把threshold_mb降低到512,让它更早开始工作。同时scan_interval_ms可以设为30,避免在容器频繁启停时CPU开销过大。

场景三:数据库服务器。MySQL、PostgreSQL等数据库对内存延迟极其敏感。这种场景下我建议谨慎使用KSM,或者把scan_interval_ms设到100以上,max_scan_pages降到500,让KSM在后台慢慢干活,不要抢占数据库的CPU。merge_across_nodes一定要设为0,避免跨NUMA节点合并带来的延迟抖动。

场景四:大内存计算节点(128GB+)。这类机器通常跑大数据、AI训练等任务,内存利用率高但重复页不一定多。建议先观察pages_sharing的增长速度,如果增长缓慢说明重复页少,KSM效果有限,不必过度调优。可以把free_mb设为2048,避免KSM在内存充裕时浪费CPU。

七、常见问题和排查思路

问题一:ksmtuned启动后pages_sharing一直是0。先确认KSM是否真的在运行(cat /sys/kernel/mm/ksm/run),再确认是否有足够的重复页。可以用smem或pmap工具查看进程的内存映射,看是否有大量相同内容的匿名页。如果是全新启动的系统,重复页本来就少,需要跑一段时间业务后才会有效果。

问题二:CPU被ksmtuned占满。直接降低scan_interval_ms到50或100,同时减小max_scan_pages到500。如果还是高,考虑在业务低峰期才让ksmtuned全速工作,高峰期限制它。

问题三:开启KSM后系统变慢。这通常是因为ksmtuned在扫描时消耗了太多CPU,导致业务进程得不到足够的CPU时间。解决办法是调大sleep_ms参数(在/etc/ksmtuned.conf中),让ksmtuned每次扫描后多休息一会儿。另外检查是否有透明大页(THP)和KSM冲突的情况,必要时关闭THP:

echo never > /sys/kernel/mm/transparent_hugepage/enabled

问题四:重启后KSM参数丢失。所有/sys/kernel/mm/ksm/下的修改都是临时的。要永久生效,必须通过ksmtuned服务来管理,或者在/etc/rc.local中加入echo命令。推荐用ksmtuned,因为它会根据内存状态动态调整,比固定参数更智能。

八、监控KSM效果的实用方法

除了前面提到的脚本,还可以用sar或vmstat来观察整体内存变化趋势。开启KSM前后对比free命令的输出:

free -h

重点关注available和used的变化。更精确的方法是看/proc/meminfo中的AnonHugePages和KSM相关统计。另外,如果你用的是CentOS 8,可以用cockpit的Web界面直接查看内存使用图表,直观看到KSM生效后的内存释放曲线。

长期来看,建议把KSM的监控纳入运维体系。用Zabbix或Prometheus采集ksm.pages_sharing和ksm.pages_saving这两个指标,设置告警阈值。当pages_sharing突然下降时,可能意味着业务发生了变化(比如虚拟机迁移了),需要排查原因。

总结

ksmtuned是CentOS系统上管理KSM内存合并的最佳工具,安装简单、配置灵活、自动化程度高。核心就是根据你的业务类型和硬件配置,调整free_mb、threshold_mb、scan_interval_ms这几个关键参数。虚拟化场景大胆开,数据库场景谨慎开,大内存场景先观察再决定。记住一个原则:KSM是用CPU换内存,CPU不紧张就多合并,CPU紧张就少合并。掌握这个平衡,你的服务器内存利用率就能上一个台阶。