管理内存是操作系统的核心职责之一。Linux系统把内存分页管理,每一页(page)的大小可以用命令getconf PAGESIZE查看,单位是字节(Byte),Linux默认的页大小为4KB。
[root@localhost ~]# getconf PAGESIZE 4096 |
内存的总体情况可以通过cat /proc/meminfo查看
[root@localhost ~]# cat /proc/meminfo MemTotal: 65751096 kB MemFree: 64077144 kB MemAvailable: 64061216 kB Buffers: 2076 kB Cached: 404000 kB SwapCached: 0 kB Active: 759688 kB Inactive: 177952 kB Active(anon): 532748 kB Inactive(anon): 17212 kB Active(file): 226940 kB Inactive(file): 160740 kB Unevictable: 0 kB Mlocked: 0 kB SwapTotal: 33554428 kB SwapFree: 33554428 kB Dirty: 8 kB Writeback: 0 kB AnonPages: 531796 kB Mapped: 54212 kB Shmem: 18396 kB Slab: 194488 kB SReclaimable: 72256 kB SUnreclaim: 122232 kB KernelStack: 13360 kB PageTables: 12020 kB NFS_Unstable: 0 kB Bounce: 0 kB WritebackTmp: 0 kB CommitLimit: 66429976 kB Committed_AS: 2371680 kB VmallocTotal: 34359738367 kB VmallocUsed: 415224 kB VmallocChunk: 34308620284 kB HardwareCorrupted: 0 kB AnonHugePages: 290816 kB HugePages_Total: 0 HugePages_Free: 0 HugePages_Rsvd: 0 HugePages_Surp: 0 Hugepagesize: 2048 kB DirectMap4k: 217920 kB DirectMap2M: 8124416 kB DirectMap1G: 60817408 kB |
本文主要总结一下在运维实践中遇到的跟内存有关的问题或技巧。
buffer cache和swap
free是系统管理员查看服务器内存情况常用的命令,在CentOS7.4上输出内容如下:
[root@localhost ~]# free total used free shared buff/cache available Mem: 65751096 1079756 64070284 18396 601056 64054484 Swap: 33554428 0 33554428 |
从CentOS7系列版本开始,buffer和cache被合并为一列显示了,在之前的版本中是分开的。虽然被合并显示,但是它们保存的内容是不同的。
l Buffer——顾名思义是缓冲区的意思,由于内存访问速度远超硬盘等块设备,所以Linux系统把文件系统中访问过的元数据缓存在buffer中,以提高下次访问的速度。
l Cache——cache是缓存区,在内存足够的情况下,Linux系统总是倾向于把打开过的程序、文件内容缓存在内存中,所以长时间运行的服务器cache都比较大,这么做是为了下次访问这些文件更快。当有程序申请内存且空闲内存不足时,系统会释放cache以腾出空间。
l Swap——在以前内存还是很金贵的资源时,为了“扩展”服务器有限的内存,Linux允许在块设备上划分出一块空间,将内存中不经常被访问的页置换到这里,腾出内存给处于活跃状态的进程,这个空间就是交换分区swap。由于硬盘比内存访问性能低,所以使用swap分区会降低性能。现在的服务器有一两百GB内存已经不稀奇了,似乎swap已经没有存在的意义了,其实不然,即使swap现在已经很少被用到了,我们在部署服务器时仍应该分配一个合理的空间给swap,有swap分区作为缓冲,可以避免系统因内存不足开始自动kill进程引起宕机,给运维人员处理内存饱和的问题争取时间,性能损失总比宕机要好。
实际情况中,有时会遇到有的服务器内存被cache占用完,系统开始把部分内存置换到swap中,导致性能急剧下降。这种情况下可以清理cache,释放出内存给进程用。清理cache的过程如下:
[root@localhost ~]# sync [root@localhost ~]# echo 3 > /proc/sys/vm/drop_caches |
为了安全,清理cache前要先执行sync,使内存中的dirty数据落盘。
drop_caches取值范围为1,2,3,
1表示清理页面缓存;
2表示清理页面缓存和目录缓存;
3表示清理页面缓存、目录缓存和inode缓存。
为了减小系统使用swap分区的倾向,还有个内核参数可以调整——swappiness。swappiness的取值范围是0到100,数值越大系统使用swap分区的倾向就越大,为0时表示完全不使用swap。鉴于上面介绍过swap分区存在的必要性,我们不应该把swappiness设置为0,而应该设置为1,让系统尽可能避免使用swap分区,同时并不完全放弃使用swap的可能。
[root@localhost ~]# echo "vm.swappiness = 1" >> /proc/sys/vm/swappiness [root@localhost ~]# sysctl -p vm.swappiness = 1 |
KSM
KSM(Kernel SamePage Merging)是一种压缩内存的技术,顾名思义就是将内存中内容相同的页进行合并以节省内存空间,当有进程需要修改该内存页的内容时,再将其拷贝一份再修改,也就是“写时复制copy on write”。KSM最早是为了优化qemu-kvm宿主机内存使用而诞生的,当一台宿主机上运行多个相同操作系统的虚拟机时,有很多内存页内容相同可以合并。KSM在CentOS6和CentOS7上是默认打开的,有两个相关服务:
l ksm
l Ksmtuned
ksmtuned依赖ksm服务,它的主体/usr/sbin/ksmtuned是一个shell脚本,通过读取配置文件/etc/ksmtuned.conf中的参数,对ksm的行为进行控制。在ksmtuned的控制下,ksm并不是一直在工作,它只在参数限定的条件达到的情况下工作,ksm是否在工作可以通过cat /sys/kernel/mm/ksm/run命令查看,0表示ksm没有在工作,1表示ksm在工作。
通过分析/usr/sbin/ksmtuned,可以了解配置文件中几个主要参数的含义:
l KSM_MONITOR_INTERVAL=60 默认值为60,单位秒,表示每隔60秒,ksmtuned工作一次,扫描系统内存使用情况并据此调整ksm参数。数值越小ksmtuned工作频率越高,对cpu消耗越多;
l KSM_SLEEP_MSEC=10 默认值为10,最小值也为10,单位毫秒,ksm扫描内存页的间隔,数值越小ksm扫描内存页的频率越高,对cpu消耗越多。对应的内核参数是/sys/kernel/mm/ksm/sleep_millisecs;
l KSM_NPAGES_BOOST=300
KSM_NPAGES_DECAY=-50
KSM_NPAGES_MIN=64
KSM_NPAGES_MAX=1250
这一组参数用于设置ksm一次扫描多少内存页,对应内核参数/sys/kernel/mm/ksm/pages_to_scan。pages_to_scan的值在KSM_NPAGES_MIN和KSM_NPAGES_MAX之间波动,如果空闲内存大于临界值,需要降低ksm工作强度,则每次ksmtuned工作都会调低pages_to_scan 300(KSM_NPAGES_BOOST)页;反之,需要增强ksm工作强度,则每次ksmtuned工作会调高pages_to_scan 50(KSM_NPAGES_DECAY)页。
l KSM_THRES_COEF=20
KSM_THRES_CONST=2048
这两个参数用于设置一个内存临界值,总内存的20%(KSM_THRES_COEF)和2048MB(KSM_THRES_CONST)两者之间取大者。当“所有qemu进程的内存”和临界值之和小于“总内存”,并且空闲内存大于临界值时,ksm停止工作,/sys/kernel/mm/ksm/run被置0;其他情况下,ksm处于工作状态,/sys/kernel/mm/ksm/run被置1。如果想让ksm早点开始工作,可以调高临界值。
常见的ksmtuned.conf配置如下:
# Configuration file for ksmtuned.
# How long ksmtuned should sleep between tuning adjustments KSM_MONITOR_INTERVAL=60
# Millisecond sleep between ksm scans for 16Gb server. # Smaller servers sleep more, bigger sleep less. KSM_SLEEP_MSEC=10
KSM_NPAGES_BOOST=300 KSM_NPAGES_DECAY=-50 KSM_NPAGES_MIN=64 KSM_NPAGES_MAX=1250
KSM_THRES_COEF=20 KSM_THRES_CONST=2048
# uncomment the following if you want ksmtuned debug info
LOGFILE=/var/log/ksmtuned DEBUG=1 |
日志/var/log/ksmtuned记录了ksmtuned每次执行的结果:
[root@localhost ~]# tail -100f /var/log/ksmtuned Thu Jan 18 11:29:18 CST 2018: total 65751096 Thu Jan 18 11:29:18 CST 2018: sleep 10 Thu Jan 18 11:29:18 CST 2018: thres 13150219 Thu Jan 18 11:30:18 CST 2018: committed 0 free 64470428 Thu Jan 18 11:30:18 CST 2018: 13150219 < 65751096 and free > 13150219, stop ksm |
我的测试机内存比较空闲,ksm一直处于stop状态,下面我把KSM_THRES_COEF调大到100然后systemctl restart ksmtuned,强行让ksm工作,然后看看效果:
[root@localhost ~]# cat /sys/kernel/mm/ksm/run 1 [root@localhost ~]# tail -10f /var/log/ksmtuned Thu Jan 18 15:05:05 CST 2018: sleep 10 Thu Jan 18 15:05:05 CST 2018: thres 65751096 Thu Jan 18 15:06:05 CST 2018: committed 0 free 64467712 Thu Jan 18 15:06:05 CST 2018: 65751096 > 65751096, start ksm Thu Jan 18 15:06:05 CST 2018: 64467712 < 65751096, boost Thu Jan 18 15:06:05 CST 2018: KSMCTL start 300 10 Thu Jan 18 15:07:05 CST 2018: committed 0 free 64468624 Thu Jan 18 15:07:05 CST 2018: 65751096 > 65751096, start ksm Thu Jan 18 15:07:05 CST 2018: 64468624 < 65751096, boost Thu Jan 18 15:07:05 CST 2018: KSMCTL start 600 10 |
/sys/kernel/mm/ksm/pages_sharing记录了有多少内存页正在使用被合并的内存页,也就是实际节省的内存页数。页数乘以PAGESIZE就可以计算出实际节省的内存,命令如下:
echo “$(($(cat /sys/kernel/mm/ksm/pages_sharing)*$(getconf PAGESIZE)/1024/1024))MB” |
NUMA
给有两路cpu的服务器插过内存的同学应该知道,每路cpu都有自己的内存条插槽。这是解决多cpu扩展问题的一种架构——NUMA,Non-Uniform Memory Access,非一致内存访问。为什么叫“非一致”呢?因为一个cpu访问自己的本地内存比访问别的cpu的内存速度要快,所以叫非一致。
numactl是一个管理numa的命令行工具,如果系统默认没有这个命令,可以通过yum install numactl安装。numactl –hardware可以查看cpu和内存的硬件情况,下面是一台4路服务器上执行命令的结果:
[root@localhost ~]# numactl --hardware available: 4 nodes (0-3) node 0 cpus: 0 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 node 0 size: 16338 MB node 0 free: 15279 MB node 1 cpus: 1 5 9 13 17 21 25 29 33 37 41 45 49 53 57 61 node 1 size: 16384 MB node 1 free: 15762 MB node 2 cpus: 2 6 10 14 18 22 26 30 34 38 42 46 50 54 58 62 node 2 size: 16384 MB node 2 free: 15646 MB node 3 cpus: 3 7 11 15 19 23 27 31 35 39 43 47 51 55 59 63 node 3 size: 16384 MB node 3 free: 15861 MB node distances: node 0 1 2 3 0: 10 20 30 20 1: 20 10 20 30 2: 30 20 10 20 3: 20 30 20 10 |
4路cpu就是4个numa nodes,每个nodes有16G的本地内存,整个服务器总共有64G物理内存。
numastat -c 可以查看指定进程的内存使用情况:
[root@localhost ~]# numastat -c nginx
Per-node process memory usage (in MBs) PID Node 0 Node 1 Node 2 Node 3 Total ------------- ------ ------ ------ ------ ----- 74675 (nginx) 0 0 1 0 1 74676 (nginx) 0 0 2 0 3 74677 (nginx) 1 0 1 0 3 74678 (nginx) 1 1 1 0 3 74679 (nginx) 0 0 2 0 3 74680 (nginx) 1 0 1 0 3 74681 (nginx) 0 1 1 0 3 74682 (nginx) 0 0 2 0 3 74683 (nginx) 1 0 1 0 3 74684 (nginx) 0 0 2 0 3 74685 (nginx) 0 1 1 0 3 74686 (nginx) 1 0 1 0 3 74687 (nginx) 0 0 2 0 3 74688 (nginx) 0 0 2 0 3 74689 (nginx) 0 1 1 0 3 74690 (nginx) 1 0 1 0 3 74691 (nginx) 0 0 2 0 3 74692 (nginx) 0 0 2 0 3 74693 (nginx) 0 1 1 0 3 74694 (nginx) 2 0 1 1 3 74695 (nginx) 0 0 2 0 3 74696 (nginx) 0 0 2 0 3 74697 (nginx) 1 1 1 0 3 74698 (nginx) 1 0 1 0 3 74699 (nginx) 0 0 2 0 3 ------------- ------ ------ ------ ------ ----- Total 18 6 35 5 65 |
上面的输出可以看出各个nginx进程的内存分散在了4个node上。
CentOS6和Centos7上都有一个叫numad的服务,这个服务负责在numa架构下调度各节点的内存分配,使内存分配具有cpu亲和性,例如一个线程运行在cpu0上,它申请的内存页将尽量从cpu0的本地内存中分配。这个服务默认没有开启,因为这种内存分配策略并不适合所有场景,当服务器上运行了很多应用程序或很多虚拟机时,numad具有cpu亲和性的内存分配方式可以提高性能,但是如果整个系统只运行一个高内存消耗的程序如数据库,numad反而会导致内存分配不均而降低性能,因此数据库服务器建议不要开启numad服务。
Linux系统中还有一个numa自动平衡策略,系统通过自动调配内存以求在各个numa nodes的内存使用率处于相对平衡的状态,这个策略可以通过修改内核参数/proc/sys/kernel/numa_balancing控制。关闭numa自动平衡策略:echo 0 > /proc/sys/kernel/numa_balancing;开启numa自动平衡策略:echo 1 > /proc/sys/kernel/numa_balancing。这个策略系统默认也没开启。
关于Linux内存的小技巧暂时只想到这么多,感谢阅读!