/proc/sys/vm/下各参数含义

虚拟内存的overcommit

Memory Overcommit的意思是操作系统承诺给进程的内存大小超过了实际可用的内存。一个保守的操作系统不会允许memory overcommit,有多少就分配多少,再申请就没有了,这其实有些浪费内存,因为进程实际使用到的内存往往比申请的内存要少,比如某个进程malloc()了200MB内存,但实际上只用到了100MB,按照UNIX/Linux的算法,物理内存页的分配发生在使用的瞬间,而不是在申请的瞬间,也就是说未用到的100MB内存根本就没有分配,这100MB内存就闲置了。下面这个概念很重要,是理解memory overcommit的关键:commit(或overcommit)针对的是内存申请,内存申请不等于内存分配,内存只在实际用到的时候才分配。

Linux是允许memory overcommit的,只要你来申请内存我就给你,寄希望于进程实际上用不到那么多内存,但万一用到那么多了呢?那就会发生类似“银行挤兑”的危机,现金(内存)不足了。Linux设计了一个OOM killer机制(OOM = out-of-memory)来处理这种危机:挑选一个进程出来杀死,以腾出部分内存,如果还不够就继续杀…也可通过设置内核参数 vm.panic_on_oom 使得发生OOM时自动重启系统

怎样才算是overcommit呢?kernel设有一个阈值,申请的内存总数超过这个阈值就算overcommit,在/proc/meminfo中可以看到这个阈值的大小:

# cat /proc/meminfo |grep Com
CommitLimit:      143152 kB
Committed_AS:       2788 kB

CommitLimit 就是overcommit的阈值,申请的内存总数超过CommitLimit的话就算是overcommit。

这个阈值是如何计算出来的呢?它既不是物理内存的大小,也不是free memory的大小,它是通过内核参数vm.overcommit_ratio或vm.overcommit_kbytes间接设置的,公式如下:
【CommitLimit = (Physical RAM * vm.overcommit_ratio / 100) + Swap】

vm.overcommit_ratio 是内核参数,缺省值是50,表示物理内存的50%。如果你不想使用比率,也可以直接指定内存的字节数大小,通过另一个内核参数 vm.overcommit_kbytes 即可;

如果使用了huge pages,那么需要从物理内存中减去,公式变成:
CommitLimit = ([total RAM] – [total huge TLB RAM]) * vm.overcommit_ratio / 100 + swap

/proc/meminfo中的 Committed_AS 表示所有进程已经申请的内存总大小,(注意是已经申请的,不是已经分配的),如果 Committed_AS 超过 CommitLimit 就表示发生了 overcommit,超出越多表示 overcommit 越严重。Committed_AS 的含义换一种说法就是,如果要绝对保证不发生OOM (out of memory) 需要多少物理内存。


overcommit_memory

此值控制内存过度提交策略:

  • 0:内核会比较用户态内存请求大小与总内存+交换分区,拒绝明显的过度提交。
  • 1:内核假设内存始终充足,直到实际耗尽。
  • 2:内核采用“永不过度提交”策略,尝试完全避免内存超限(受 user_reserve_kbytes 影响)。
    此功能对许多程序有用,因为它们会通过 malloc 预分配大量内存备用,但实际使用较少。
    默认值:0。
    参考:https://www.kernel.org/doc/html/latest/mm/overcommit-accounting.html 和 mm/util.c::__vm_enough_memory()。

overcommit_ratio

当 overcommit_memory 设置为 2 时,已提交的地址空间不得超过交换分区大小加上此参数指定的物理内存百分比。


overcommit_kbytes

当 overcommit_memory 设置为 2 时,已提交的地址空间不得超过交换分区大小加上此参数指定的物理内存量(单位:KB)。
注意:overcommit_kbytes 与 overcommit_ratio 互斥,同一时间只能生效一个。设置其中一个会使另一个失效(读取时显示为 0)。


admin_reserve_kbytes

系统中应为具备 cap_sys_admin 能力的用户保留的可用内存数量。

默认值: min(空闲页的3%, 8MB)
此值应足以让管理员在默认的 overcommit “guess” 模式下登录并终止进程(如有必要)。

注意:在 overcommit 设置为 “never” 的系统中,需增加此值以覆盖恢复操作所用程序的完整虚拟内存大小。否则,root 可能无法登录以恢复系统。


如何计算最小有效预留?

1. 基础组件:
sshd 或 login + bash(或其他 shell) + top(或 ps、kill 等工具)

2. overcommit “guess” 模式:

  • 计算各组件的驻留集大小(RSS)总和。
  • 在 x86_64 架构上约为 8MB。

3. overcommit “never” 模式:

  • 取各组件虚拟大小(VSZ)的最大值,并加上 RSS 总和。
  • 在 x86_64 架构上约为 128MB。

生效时机:
修改此参数后,当任意应用程序请求内存时立即生效。


user_reserve_kbytes

当 overcommit_memory 设置为 2(“永不超额提交”模式)时,系统会保留当前进程大小的 3% 与 user_reserve_kbytes 中的较小值作为自由内存的预留空间。此机制旨在防止用户启动单个占用大量内存的进程,导致无法恢复(例如杀死该进程)。
user_reserve_kbytes 默认值为当前进程大小的 3% 与 128MB 中的较小值。
若将其减少为 0,则允许用户进程分配除 admin_reserve_kbytes 外的所有可用内存。任何后续命令均会因内存不足失败,提示 “fork: Cannot allocate memory”。
修改此参数会在应用程序请求内存时立即生效。


内存整理

1、关于内存整理
内存的伙伴系统以页为单位进行内存管理,经过系统和应用程序的大量申请释放,就会造成大量离散的页面,这就是内存碎片的来源。而一些应用程序和硬件需要物理地址连续的内存,即使内存总量够但内存分配依然会失败,此时就需要进行内存整理,把部分内存迁移整合,从而腾出连续内存供应用使用。


2、触发内存整理
触发内存整理分为两种方式,主动式和被动式,所谓主动式就是通过/proc/sys/vm/compact_memory接口主动触发一次内存整理;而被动式就是系统在分配内存时,当前系统内存分布无法满足应用需求,此时就会触发系统被动的进行一次内存整理,整理完再进行内存的分配。


3、compact_memory和extfrag_threshold
该接口只有在启用CONFIG_COMPACTION编译选项时才生效。只要往/proc/sys/vm/compact_memory中写入1,就会触发所有的内存域压缩,使空闲的内存尽可能形成连续的内存块。而extfrag_threshold则是控制进行内存整理的意向,作用于内存分配路径中,该值设置得越小,越倾向于进行内存整理。


compact_memory

仅当配置 CONFIG_COMPACTION 时可用。向文件中写入 1 时,所有内存区域将被压缩,使空闲内存尽可能以连续块形式存在。例如,这对分配大页(huge pages)至关重要,尽管进程也可根据需要自行压缩内存。


compaction_proactiveness

此可调参数的取值范围为 [0, 100],默认值为 20。该参数决定后台压缩的激进程度——较高的值表示更积极主动地进行内存紧缩,但也可能增加系统负载。较低的值则表示更保守的紧缩策略,可能导致内存碎片的累积。向此文件写入非零值会立即触发主动压缩;设置为 0 则禁用主动压缩。

注意:压缩会对系统产生显著影响,因为不同进程的页面会被移动,可能导致未预料的应用出现延迟峰值。内核会通过启发式算法避免浪费 CPU 周期(若检测到主动压缩未生效)。
谨慎设置极端值(如 100),因为这可能导致后台压缩活动过于频繁。


compact_unevictable_allowed

仅当配置 CONFIG_COMPACTION 时可用。设置为 1 时,允许压缩检查不可逐出的 LRU 页面(如被锁定的页面 mlocked pages)以进行压缩。适用于允许小页故障停顿以换取大连续空闲内存的系统。设置为 0 可阻止压缩移动不可逐出的页面。默认值为 1。

在 CONFIG_PREEMPT_RT 配置下,默认值为 0,以避免因压缩导致页故障(可能阻塞任务激活直至故障解决)。


extfrag_threshold

此参数影响内核是否会通过紧凑内存或直接回收来满足高阶分配请求。debugfs 中的 extfrag/extfrag_index 文件显示系统各区域不同阶的碎片化指数:

  • 接近 0:表示因内存不足导致分配失败;
  • 接近 1000:表示因碎片化导致分配失败;
  • -1:表示只要满足水位阈值,分配总能成功。
    若某区域的碎片化指数 ≤ extfrag_threshold,内核不会紧凑该区域的内存。默认值为 500。

extfrag_index

extfrag_index表示的是内存分配失败是由于内存不足还是碎片化引起。

# cat /proc/buddyinfo
Node 0, zone      DMA     13     18     14      6      7      3      2      6      6      5     55

# cat extfrag/extfrag_index
Node 0, zone      DMA -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000

unusable_index

unusable_index展示了某类大小的内存(order)有多少free内存是没有使用的。它表示对某order分配内存的时候,由于碎片化原因接近失败的程度

# cat extfrag/unusable_index
Node 0, zone      DMA 0.000 0.000 0.000 0.001 0.002 0.004 0.006 0.008 0.020 0.045 0.087 

上面数值越接近于1,表示越无法申请对应order的内存


defrag_mode(5.10.x已不存在)

设置为 1 时,页面分配器会更积极地避免内存碎片化,并维持生成大页(huge pages)或高阶页面的能力。
建议在系统启动后立即启用此参数,因为一旦发生碎片化,其影响可能长期存在甚至不可逆。


磁盘缓存和脏页回刷

文件缓存是一项重要的性能改进,在大多数情况下,读缓存在绝大多数情况下是有益无害的(程序可以直接从RAM中读取数据)。写缓存比较复杂,Linux内核将磁盘写入缓存,过段时间再异步将它们刷新到磁盘。这对加速磁盘I/O有很好的效果,但是当数据未写入磁盘时,丢失数据的可能性会增加。

当然,也存在缓存被写爆的情况。还可能出现一次性往磁盘写入过多数据,以致使系统卡顿。这些卡顿是因为系统认为,缓存太大用异步的方式来不及把它们都写进磁盘,于是切换到同步的方式写入。

这些都是可控制的选项,根据工作负载和数据,你可以决定如何设置它们:


dirty_background_bytes

包含后台内核刷写线程开始写回脏数据的脏内存阈值(以字节为单位)。
注意
dirty_background_bytes 是 dirty_background_ratio 的对应参数,两者不可同时生效。写入其中一个参数后,系统会立即基于该值评估脏内存限制,而另一个参数读取时会显示为 0。


dirty_background_ratio

表示脏数据占可用内存(包含空闲页和可回收页)的百分比阈值,达到此比例时后台内核刷写线程将开始写回脏数据。
注意

  • 可用内存(total available memory)不等于系统总内存。
  • dirty_background_ratio 与 dirty_background_bytes 互斥,仅能生效一个。

dirty_bytes

包含进程自行触发写回脏数据的脏内存阈值(以字节为单位)。
注意

  • dirty_bytes 是 dirty_ratio 的对应参数,两者不可同时生效。写入其中一个参数后,系统会立即基于该值评估脏内存限制,而另一个参数读取时会显示为 0。
  • 最小允许值为两页(以字节计算),低于此值的设置会被忽略,并保留原有配置。

dirty_ratio

表示脏数据占可用内存(包含空闲页和可回收页)的百分比阈值,达到此比例时生成磁盘写入的进程将自行开始写回脏数据。
注意:

  • 可用内存(total available memory)不等于系统总内存。
  • dirty_ratio 与 dirty_bytes 互斥,仅能生效一个。

如何理解dirty_radio和dirty_background_ratio

# cat dirty_background_ratio
10
# cat dirty_ratio
20

dirty_background_ratio :是内存可以填充“脏数据”的百分比。这些“脏数据”在稍后是会写入磁盘的,pdflush/flush/kdmflush这些后台进程会稍后清理脏数据。举一个例子,我有32G内存,那么有3.2G的内存可以待着内存里,超过3.2G的话就会有后来进程来清理它

dirty_ratio :是绝对的脏数据限制,内存里的脏数据百分比不能超过这个值。如果脏数据超过这个数量,新的IO请求将会被阻挡,直到脏数据被写进磁盘。这是造成IO卡顿的重要原因,但这也是保证内存中不会存在过量脏数据的保护机制


dirty_expire_centisecs

定义脏数据符合内核刷写线程写出条件的最长存活时间,单位为百分之一秒(即 1 个单位 = 0.01 秒)。当数据在内存中保持脏状态超过此间隔时,下次刷写线程唤醒时将被写出。

即:脏数据能存活的时间。默认值是30秒。当 pdflush/flush/kdmflush 进行起来时,它会检查是否有数据超过这个时限,如果有则会把它异步地写到磁盘中。毕竟数据在内存里待太久也会有丢失风险


dirty_writeback_centisecs

内核刷写线程会定期唤醒并将旧数据写入磁盘。此可调参数表示两次唤醒之间的时间间隔,单位为百分之一秒(1 个单位 = 0.01 秒)(默认是5秒)。
将其设置为 0 将完全禁用周期性写回


dirtytime_expire_seconds

当懒标记时间(lazytime)索引节点的页面持续被污染时,带有更新时间戳的索引节点可能永远无法获得写回磁盘的机会。此外,如果文件系统中唯一发生的变更是由访问时间(atime)更新导致的脏时间索引节点,系统将调度一个工作线程以确保该索引节点最终被推送到磁盘。此可调参数用于定义脏索引节点何时达到由内核刷写线程执行写回的“老龄”条件,同时也用作唤醒 dirtytime_writeback 线程的时间间隔。

上面是机翻的原文,说人话就是:主要是给 lazytime inode 设置的过期时间,比如 inode 只是更新了 atime,这种更新非常频繁的数据就没必要短时间就更新,而且负责刷盘的是另外一个专门的 dirtytime writeback 进程,因此这个的默认时间比较长:12 小时


drop_caches

向此文件写入值会导致内核丢弃干净的缓存(如页面缓存),并回收可回收的 slab 对象(如目录项 dentry 和索引节点 inode)。丢弃后,这些对象的内存将被释放。

  • 释放页面缓存:
    echo 1 > /proc/sys/vm/drop_caches
  • 释放可回收的 slab 对象(包括目录项和索引节点):
    echo 2 > /proc/sys/vm/drop_caches
  • 释放 slab 对象和页面缓存:

echo 3 > /proc/sys/vm/drop_caches
此操作是非破坏性的,不会释放任何脏对象。若要增加被释放的对象数量,用户可在写入 /proc/sys/vm/drop_caches 前执行 sync 命令。这会减少系统中的脏对象数量,从而增加可丢弃的候选对象。

注意:

  • 此文件并非用于控制各类内核缓存(如索引节点、目录项、页面缓存等)的增长。这些对象会在系统其他部分需要内存时由内核自动回收。
  • 使用此文件可能导致性能问题。由于它会丢弃缓存对象,重新创建这些对象(尤其是高频使用的对象)可能消耗大量 I/O 和 CPU 资源。因此,不建议在测试或调试环境外的场景中使用。

日志信息:
使用此文件时,内核日志中可能出现类似以下信息:
cat (1234): drop_caches: 3
这些仅为信息记录,不代表系统存在问题。若需禁用此类日志,可向 drop_caches 写入 4(即设置第 2 位)。


vfs_cache_pressure

此百分比值控制内核回收用于目录项(dentry)和 inode 对象缓存的内存的倾向性。

默认值 vfs_cache_pressure=100 时,内核会以与页缓存(pagecache)和交换缓存(swapcache)“公平”的速度回收 dentry 和 inode。降低此值会使内核更倾向于保留 dentry 和 inode 缓存;若设置为 0,内核将因内存压力完全停止回收 dentry 和 inode,这可能导致内存耗尽(OOM)。高于 100 的值会使内核优先回收 dentry 和 inode 缓存。

将 vfs_cache_pressure 显著提升至 100 以上可能对性能产生负面影响。回收代码需获取多种锁以查找可释放的目录和 inode 对象,例如 vfs_cache_pressure=1000 时,系统会尝试回收比实际需求多 10 倍的可释放对象。


水线、保留内存和oom

min_free_kbytes设的越大,watermark的线越高,同时三个线之间的buffer量也相应会增加。这意味着会较早的启动kswapd进行回收,且会回收上来较多的内存(直至watermark[high]才会停止),这会使得系统预留过多的空闲内存,从而在一定程度上降低了应用程序可使用的内存量。极端情况下设置min_free_kbytes接近内存大小时,留给应用程序的内存就会太少而可能会频繁地导致OOM的发生。

min_free_kbytes设的过小,则会导致系统预留内存过小。kswapd回收的过程中也会有少量的内存分配行为(会设上PF_MEMALLOC)标志,这个标志会允许kswapd使用预留内存;另外一种情况是被OOM选中杀死的进程在退出过程中,如果需要申请内存也可以使用预留部分。这两种情况下让他们使用预留内存可以避免系统进入deadlock状态


min_free_kbytes

最小空闲内存(单位:KB)
强制 Linux 虚拟内存(VM)保留的最小空闲内存量。VM 使用此值计算系统中每个低内存区的 watermark[WMARK_MIN] 阈值,按比例为每个低内存区保留一定数量的空闲页。

警告:

  • 若值低于 1024KB,系统可能出现隐蔽故障,并在高负载下易发生死锁。
  • 若值设置过高,可能导致系统立即触发 OOM(内存不足终止进程)。

watermark_boost_factor

此因子控制内存碎片化时的回收力度。当不同移动性的页混合存在于页块(pageblock)中时,它定义了需回收的高位水印(high watermark)百分比。其目标是减少未来内存紧凑(compaction)的工作量,并提高高阶分配(如 SLUB 分配、透明大页 THP 和 hugetlbfs 页)的成功率。

为与 watermark_scale_factor 参数协同工作,此值的单位为万分之一(1/10,000)。默认值 15,000 表示当页块因碎片化混合时,最多回收高位水印的 150%。回收量由近期发生的碎片化事件次数决定。若此值小于一个页块大小(例如 64 位 x86 系统的 2MB),则至少回收一个页块的页数。设置为 0 将禁用此特性。


watermark_scale_factor

此因子控制 kswapd(内核交换守护进程)的激进程度,定义节点/系统中需保留的内存量,以决定何时唤醒或休眠 kswapd。

单位为万分之一(1/10,000),默认值 10 表示水位标记间距为节点/系统可用内存的 0.1%。最大值为 3000(即 30% 内存)。

若线程频繁触发直接回收(allocstall)或 kswapd 过早休眠(kswapd_low_wmark_hit_quickly),可能表明 kswapd 为降低延迟所维护的自由页数过少,无法应对系统的分配突发。此时可通过调整此参数调节 kswapd 的激进程度。
实测:

# echo 1000 > watermark_scale_factor
pages free     61643
        min      529
        low      7686  间隔变大了(10%的managed)
        high     14843
        spanned  77568
        present  77568
        managed  71576
# echo 10 > watermark_scale_factor
pages free     61658
        min      529
        low      661
        high     793
        spanned  77568
        present  77568
        managed  71576

lowmem_reserve_ratio

场景与风险
在高内存(highmem)机器的某些专用工作负载中,允许进程从“低内存”区域分配内存可能是危险的。因为此类内存可能通过 mlock() 系统调用被锁定,或因交换空间不足而无法回收。
在大型高内存机器上,缺乏可回收的低内存可能导致严重后果。

内核保护机制
Linux 页面分配器通过一种机制防止可能使用高内存的分配过度消耗低内存。这意味着部分低内存会被保护,避免被锁定到用户空间的不可回收内存中。
(类似地,旧的 16MB ISA DMA 区域也受此机制保护,防止被高内存或低内存分配占用。)

参数作用
lowmem_reserve_ratio 决定内核在保护低内存区域时的激进程度。

中译中:
除了min_free_kbytes会在每个zone上预留一部分内存外,lowmem_reserve_ratio是在各个zone之间进行一定的防卫预留,主要是防止高端zone在没内存的情况下过度使用低端zone的内存资源。

例如现在常见的一个node的机器有三个zone: DMA,DMA32和NORMAL。DMA和DMA32属于低端zone,内存也较小,如96G内存的机器两个zone总和才1G左右,NORMAL就相对属于高端内存(现在一般没有HIGH zone),而且数量较大(>90G)。低端内存有一定的特殊作用比如发生DMA时只能分配DMA zone的低端内存,因此需要在 尽量可以使用高端内存时 而 不使用低端内存,同时防止高端内存分配不足的时候抢占稀有的低端内存。

典型应用场景:

  • 机器使用高内存或 ISA DMA;
  • 应用程序使用 mlock();
  • 系统未配置交换空间。
    此时应调整 lowmem_reserve_ratio 的值。

参数结构
lowmem_reserve_ratio 是一个数组,可通过读取文件查看:
需要注意的是,lowmem_reserve_ratio只是一个系数,并不是直接可用的数值,内核函数setup_per_zone_lowmem_reserve() 通过lowmem_reserve_ratio这个系数来计算出各个zone的保留内存。

% cat /proc/sys/vm/lowmem_reserve_ratio
256     256     32

实际计算逻辑
内核并非直接使用这些值,而是通过公式计算每个内存区的“保护页数”(Protection Pages)。例如,在 /proc/zoneinfo 中可能显示如下内容(以 x86-64 为例):

Node 0, zone      DMA
  pages free     1355
        min      3
        low      3
        high     4
      :
      :
    numa_other   0
        protection: (0, 2004, 2004, 2004)
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  pagesets
    cpu: 0 pcp: 0
        :

保护页数会与水位(watermark)结合,判断是否允许从该内存区分配页面。例如:

  • 若请求普通[NORMAL]页面(索引=2),且当前空闲页数(1355)小于 watermark + protection[2](4 + 2004 = 2008),则跳过此内存区;
  • 若请求 DMA 页面(索引=0),则仅使用 protection[0](值为 0)。

计算公式
对于 zone[i] 的保护值 protection[j]:

  • 当 i < j:
    protection[j] = ∑(从 zone[i+1] 到 zone[j] 的 managed_pages) / lowmem_reserve_ratio[i]
  • 当 i = j:保护值为 0(无需保护自身);
  • 当 i > j:无意义,通常为 0。

默认值

  • DMA 或 DMA32 区域:256(即 1/256,约占高内存区总页数的 0.39%);
  • 其他区域:32。

调整建议

  • 减小 lowmem_reserve_ratio 的值可保护更多低内存页(例如设为 1 表示保护 100%);
  • 值小于 1 时完全禁用保护。

在我的实际设备中如下:

pages free     61658
        min      529
        low      661
        high     793
        spanned  77568
        present  77568
        managed  71576
        protection: (0[DMA], 0[DMA32], 0[NORMAL], 0)--->相当于没有保留

oom_dump_tasks

启用后,当内核触发 OOM 杀进程时,会生成系统范围的任务转储(不包括内核线程),包含以下信息:
pid, uid, tgid, vm size, rss, pgtables_bytes, swapents, oom_score_adj 和任务名称。
此功能有助于诊断 OOM 触发原因、识别导致问题的进程,以及理解 OOM 杀手的选择逻辑。

  • 设置为 0:抑制此信息。对于拥有数千任务的超大型系统,可能不希望在 OOM 时因记录所有任务状态而产生性能损耗。
  • 设置为非零:每次 OOM 杀手实际终止内存占用过高的进程时,均会输出此信息。

默认值:1(启用)。


oom_kill_allocating_task

控制是否在 OOM 时终止触发内存分配失败的进程。

  • 设置为 0:OOM 杀手会扫描整个任务列表,基于启发式策略选择进程终止(通常选择占用内存过多且释放后能回收大量内存的进程)。
  • 设置为非零:直接终止触发 OOM 的进程,避免扫描任务列表的开销。
    若 panic_on_oom 被启用,则优先于此参数设置。

默认值:0


panic_on_oom

此参数用于启用或禁用内存不足(OOM)时的内核恐慌机制。

  • 设置为0:内核会终止某些异常进程(由OOM杀手处理)。通常OOM杀手能清理异常进程并保证系统存活。
  • 设置为1:当发生OOM时,内核会恐慌。但若某进程通过mempolicy/cpusets限制使用特定节点内存,且仅这些节点耗尽内存,则OOM杀手可能终止单个进程而非触发恐慌(因其他节点内存可能仍充足,系统整体状态未必致命)。
  • 设置为2:即使上述情况也会强制触发内核恐慌,包括内存隔离场景(如内存cgroup)下的OOM。

默认值:0

注意:1和2适用于集群故障切换场景,需根据故障策略选择。
panic_on_oom=2+kdump可提供强调试工具(如崩溃快照)以分析OOM原因。


mmap相关

mmap_min_addr

mmap 起始地址限制
限制用户进程可通过 mmap 映射的最低地址空间。由于内核空指针解引用漏洞可能基于用户空间前几页内存的信息,禁止用户进程写入这些区域。默认值为 0(无保护),设置为 64k 可允许多数应用正常运行,同时防范潜在的内核漏洞。


mmap_rnd_bits

mmap 随机化位数
用于选择 mmap 分配的虚拟内存区域(VMA)基址的随机偏移位数,仅适用于支持地址空间随机化(ASLR)的架构。此值受限于架构支持的最小/最大值,可通过 /proc/sys/vm/mmap_rnd_bits 动态调整。


mmap_rnd_compat_bits

兼容模式 mmap 随机化位数
用于选择运行在兼容模式下的应用通过 mmap 分配的 VMA 基址的随机偏移位数,仅适用于支持 ASLR 的架构。此值受限于架构支持的最小/最大值,可通过 /proc/sys/vm/mmap_rnd_compat_bits 动态调整。


max_map_count

此文件定义进程可拥有的最大内存映射区域数量。内存映射区域可能由以下操作产生:malloc 的副作用、直接调用 mmap/mprotect/madvise,或加载共享库。

核心作用:限制 VMA 数量:每个内存映射(如 mmap、共享库、堆、栈等)都会创建一个 VMA 条目,该参数限制单个进程的 VMA 总数。防止资源耗尽:避免进程因创建过多 VMA 导致内核内存耗尽或性能下降。

典型场景

  • 多数应用只需不到一千个映射;
  • 某些程序(如 malloc 调试器)可能消耗大量映射(例如每次分配生成一两个映射)。

默认值:65530。


legacy_va_layout

若此 sysctl 参数非零,将禁用新的 32 位 mmap 布局,内核会为所有进程使用旧版(2.4 内核)的地址空间布局。

1. 核心作用
虚拟地址空间布局模式:控制用户进程的虚拟内存区域(如堆、栈、共享库)的排列方式。
0(默认值):启用现代布局(支持 ASLR 地址空间随机化,提高安全性)。
1:强制使用传统布局(类似 Linux 2.6 之前的模式,禁用 ASLR)。

2. 历史背景与兼容性
传统模式(legacy_va_layout=1):
映射行为:将 mmap 区域从高地址向低地址扩展,堆(brk)从低地址向高地址增长。
问题:堆与 mmap 区域可能在中间碰撞,导致内存碎片或 mmap 失败。
现代模式(legacy_va_layout=0):
映射优化:mmap 区域默认从低地址(但高于堆顶)向高地址扩展,减少碰撞风险(需配合 ASLR)。

3. 与 ASLR 的关系
ASLR(地址空间布局随机化):
安全机制:随机化栈、堆、共享库的加载地址,防止攻击者预测内存布局。
依赖条件:现代模式下(legacy_va_layout=0)默认启用 ASLR。
传统模式冲突:若强制启用传统布局(legacy_va_layout=1),ASLR 可能被部分或完全禁用。


swap相关

swappiness

此参数定义交换(swap)与文件系统分页的相对IO成本,取值范围0-200。

  • 100:假设两者IO成本相等,内存压力会均衡作用于页缓存和交换页。
  • <100:交换IO更昂贵,倾向保留内存。
  • >100:交换IO更廉价,倾向交换。

默认值:60。

优化建议

  • 对于内存交换(如zram/zswap)或交换位于比文件系统更快的设备时,可设置>100。例如,若交换设备随机IO速度是文件系统的2倍,则swappiness=133(x+2x=200,2x=133.33)。
  • 0:仅在自由页和文件页低于区高水位时触发交换。

page-cluster

1. 核心作用
定义每次换页操作的页面数量:
该参数值为 2 的幂次方,表示系统在换入(swap-in)或换出(swap-out)内存时,一次性处理的 连续页面数量。

计算公式:
实际页面数 = 2^page_cluster
例如,默认值 3 对应 2^3 = 8 个页面(每个页面通常为 4KB,即一次处理 32KB)。

2. 默认值与范围
典型默认值:3(即每次处理 8 个页面)。
有效范围:0(1 页) 到 10(1024 页,即 4MB)。

3. 性能影响
(1) 增大参数值(如 5 → 32 页/128KB)
优势:
减少交换操作的 I/O 次数(批量处理)。
适合顺序访问模式(如大数据处理、科学计算)。
风险:
增加单次 I/O 延迟(尤其机械硬盘)。
可能预读无用页面(随机访问场景浪费内存)。

(2) 减小参数值(如 1 → 2 页/8KB)
优势:
快速响应单页请求(如交互式应用、GUI 桌面)。
减少内存浪费(避免加载不需要的页面)。
风险:
高频率的小 I/O 操作(降低 SSD 寿命/机械盘性能)。


大页相关

nr_hugepages

更改大页内存池的最小大小
详见 https://www.kernel.org/doc/html/latest/admin-guide/mm/hugetlbpage.html


hugetlb_optimize_vmemmap(5.10.x已不存在)

此选项在 struct page 结构体(定义于 include/linux/mm_types.h)的大小非 2 的幂次方时不可用(异常系统配置可能导致此情况)。
启用(设为 1)或禁用(设为 0)HugeTLB Vmemmap 优化(HVO)。

  • 启用后:从伙伴分配器后续分配的 HugeTLB 页面的 vmemmap 页将按优化规则分配(每 2MB HugeTLB 页对应 7 个 vmemmap 页,每 1GB HugeTLB 页对应 4095 个 vmemmap 页),但已分配的 HugeTLB 页不会优化。当这些优化过的 HugeTLB 页从大页池释放回伙伴分配器时,代表该范围的 vmemmap 页需重新映射,且先前丢弃的 vmemmap 页需重新分配。

    适用场景:若 HugeTLB 页是“即时”分配的(例如未显式通过 nr_hugepages 分配,而是仅设置 nr_overcommit_hugepages,导致超额提交的 HugeTLB 页动态分配),需权衡内存节省与分配/释放 HugeTLB 页时的额外开销(比未优化时慢约 2 倍)。
    注意:系统内存压力较大时,可能因 vmemmap 页分配失败而无法将 HugeTLB 页从大页池释放回伙伴分配器,需稍后重试。

  • 禁用后:从伙伴分配器后续分配的 HugeTLB 页的 vmemmap 页不再优化,分配时的额外开销消失,但已优化的 HugeTLB 页不受影响。

    清除优化:若需确保系统中无优化过的 HugeTLB 页,可先将 nr_hugepages 设为 0,再禁用此选项。注意,将 nr_hugepages 写为 0 会使所有“正在使用”的 HugeTLB 页变为过剩页,这些过剩页仍保持优化状态直至不再使用。需等待这些页释放后,系统中才无优化页。


nr_hugepages_mempolicy

在特定 NUMA 节点集上运行时调整大页池大小
详见 https://www.kernel.org/doc/html/latest/admin-guide/mm/hugetlbpage.html


nr_overcommit_hugepages

更改大页池的最大大小。最大值为 nr_hugepages + nr_overcommit_hugepages。
详见 https://www.kernel.org/doc/html/latest/admin-guide/mm/hugetlbpage.html


hugetlb_shm_group

hugetlb_shm_group 包含允许创建使用大页(Hugetlb)的 SysV 共享内存段的组 ID。


NUMA

min_slab_ratio

最小 slab 比例(仅 NUMA 内核)
定义为每个内存区总页数的百分比。当进行内存区回收(从本地区回退到全局回收)时,若某区可回收的 slab 页占比超过此值,则回收 slab 页。此机制确保 slab 增长在 NUMA 系统中受控,即使全局回收较少发生。默认值为 5%。

注意
slab 回收按内存区/节点触发,当前回收流程不限定节点且可能较慢。


min_unmapped_ratio

最小未映射页比例(仅 NUMA 内核)
定义为每个内存区总页数的百分比。仅当某区可回收的未映射页占比超过此值时,才触发内存区回收。

  • 若 zone_reclaim_mode 包含值 4,则比较对象包括文件支持的未映射页(含交换缓存页和 tmpfs 文件)。
  • 否则,仅考虑普通文件支持的未映射页(排除 tmpfs 等)。
    默认值为 1%。

numa_zonelist_order

此 sysctl 仅适用于 NUMA 且已被弃用。除节点顺序外,其他任何顺序均会导致失败!
“内存分配来源”由 zonelist 控制。
(此文档忽略 ZONE_HIGHMEM/ZONE_DMA32 以简化说明。您可能可以将 ZONE_DMA 视为 ZONE_DMA32…)

非 NUMA 场景:GFP_KERNEL 的 zonelist 顺序如下:
ZONE_NORMAL -> ZONE_DMA
这意味着,GFP_KERNEL 的内存分配请求仅在 ZONE_NORMAL 不可用时才会从 ZONE_DMA 分配。

NUMA 场景:假设为双节点 NUMA,且以下是节点 0 的 GFP_KERNEL 的 zonelist,可定义两种顺序类型:

  • (A) 节点顺序:Node(0) ZONE_NORMAL -> Node(0) ZONE_DMA -> Node(1) ZONE_NORMAL
  • (B) 区域顺序:Node(0) ZONE_NORMAL -> Node(1) ZONE_NORMAL -> Node(0) ZONE_DMA

类型 (A) 为节点 0 上的进程提供最佳局部性,但会在 ZONE_NORMAL 耗尽前使用 ZONE_DMA,这会增加 ZONE_DMA OOM(内存不足)的风险(因 ZONE_DMA 通常较小)。

类型 (B) 无法提供最佳局部性,但对 ZONE_DMA 的 OOM 更鲁棒。

  • 节点顺序([Nn]ode):按节点排序,再按节点内的区域排序。
  • 区域顺序([Zz]one):按区域类型排序,再按区域内的节点排序。
  • 指定 [Dd]efault 以请求自动配置。

默认行为

  • 32 位系统:需为内核保留 ZONE_NORMAL,因此选择区域顺序。
  • 64 位系统:需要 DMA32/DMA 的设备较少,因此选择节点顺序。
    建议保持默认顺序,除非系统/应用程序明确需要调整。

numa_stat

此接口允许运行时配置NUMA统计信息。

  • 当页分配性能成为瓶颈且可容忍工具异常/精度下降时:
    echo 0 > /proc/sys/vm/numa_stat
  • 当页分配性能正常且需保证工具兼容性时:
    echo 1 > /proc/sys/vm/numa_stat

zone_reclaim_mode

zone_reclaim_mode 允许设置当内存区域(zone)耗尽时回收内存的策略激进程度。若设置为 0,则不进行区域回收,分配将从其他区域或节点满足。
此值为以下选项的按位或组合:
1. 启用区域回收
2. 回收时写出脏页
3. 回收时交换页面

默认情况下,zone_reclaim_mode 被禁用。对于文件服务器或受益于数据缓存的工作负载,应保持禁用,因为缓存效果通常比数据局部性更重要。
启用建议:

  • 若已知工作负载按 NUMA 节点分区且访问远程内存会导致明显性能下降,可启用部分或全部回收模式。分配器会在跨节点分配前采取额外操作。
  • 允许回收写出脏页:可限制大量写入数据的进程污染其他节点的内存。当区域填满时,回收会写出脏页以限制进程,虽可能降低单个进程性能(无法占用全部系统内存缓冲写操作),但可保护其他节点的内存,避免影响其他进程。
  • 允许常规交换:限制分配仅在本地节点发生,除非被内存策略或 cpuset 配置显式覆盖。

其他未归类

enable_soft_offline(5.10.x已不存在)

可纠正的内存错误在服务器上非常常见。软下线(soft-offline)是内核针对存在(过多)已纠正内存错误的内存页面的解决方案。

针对不同类型的页面,软下线的行为和代价不同:

  • 原始错误页面:软下线会将正在使用的页面内容迁移到新的原始页面。

  • 透明大页(Transparent HugePage)中的页面:软下线会先将透明大页拆分为原始页面,然后仅迁移存在错误的原始页面。结果是用户透明地减少了一个支持的大页,可能影响内存访问性能。

  • HugeTLB 大页中的页面:软下线会先迁移整个 HugeTLB 大页(需消耗一个空闲大页作为迁移目标),随后原大页会被拆解为原始页面且无补偿,导致 HugeTLB 池容量减少 1。

在透明大页和 HugeTLB 场景中,用户需权衡可靠性(避免使用不稳定物理内存)与性能/容量的影响。

对所有架构,enable_soft_offline 控制是否启用软下线机制。设置为 1 时,内核会在需要时尝试软下线页面;设置为 0 时,内核会对软下线请求返回 EOPNOTSUPP(操作不支持)。默认值为 1。

需注意:将 enable_soft_offline 设为 0 后,以下软下线请求将不会执行:

  • 来自 RAS 可纠正错误收集器的页面软下线请求;
  • 在 ARM 架构上,来自 GHES 驱动的页面软下线请求;
  • 在 PA-RISC 架构上,来自页面回收表(Page Deallocation Table)的页面软下线请求。

highmem_is_dirtyable

仅适用于启用 CONFIG_HIGHMEM 的系统(32位系统)
此参数控制高内存是否被纳入脏页写入节流机制。默认情况下不启用,即仅内核可直接可见/可用的低内存会被计入脏页。这可能导致以下问题:

  • 在大内存系统中,若低内存基本耗尽,写入者可能过早触发节流,导致流式写入变慢;
  • 修改此值为非零:允许更多内存被标记为脏页,使写入者能更高效地将数据刷入存储。但需注意风险:某些写入操作(如直接块设备写入)仅能使用低内存,可能因无节流限制而填满低内存的脏页,从而增加 OOM 杀手触发的概率。

laptop_mode

laptop_mode 是一个控制“笔记本模式”的开关。该模式控制的所有内容详见《如何使用 Laptop Mode 节省电池电量》。


mem_profiling(5.10.x已不存在)

内存分析(需开启 CONFIG_MEM_ALLOC_PROFILING=y)

  • 1:启用内存分析功能。
  • 0:禁用内存分析功能。

说明
启用内存分析会为所有内存分配操作引入少量性能开销。默认值由 CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT 配置决定。


memory_failure_early_kill

内存错误早期终止策略
控制硬件后台检测到不可纠正内存错误(通常是内存条的双位错误)且内核无法处理时如何终止进程。在某些情况下(例如页面在磁盘上仍有有效副本),内核会透明处理错误,不影响应用;若数据无其他最新副本,则终止进程以防止数据损坏扩散。

  • 1:检测到错误后立即终止映射了受损且不可重新加载页面的所有进程。注:内核内部分配数据或交换缓存页等少数类型页面不支持此策略,但对多数用户页有效。
  • 0:仅从所有进程中取消映射受损页面,仅当进程尝试访问该页面时终止目标进程。

实现细节
通过可捕获的 SIGBUS 信号(带 BUS_MCEERR_AO 标志)终止进程,允许进程自行处理。此功能仅在支持高级机器检查处理的硬件平台上激活,且依赖硬件能力。应用程序可通过 PR_MCE_KILL 系统调用(prctl)单独覆盖此设置。注:需开启CONFIG_MEMORY_FAILURE


memory_failure_recovery

内存故障恢复(需平台支持)
注:需开启CONFIG_MEMORY_FAILURE

  • 1:尝试恢复。
  • 0:内存故障时直接触发内核恐慌(panic)。

nr_trim_pages

仅适用于 NOMMU 内核
调整对齐到 2 的幂次方的 NOMMU 映射分配的多余页面修剪行为。

  • 值为 0:完全禁用分配修剪。
  • 值为 1:激进修剪多余页面。
  • 值 ≥ 1:作为触发修剪的阈值(水印)。

默认值:1
详见 https://www.kernel.org/doc/html/latest/admin-guide/mm/nommu-mmap.html


page_lock_unfairness

此值决定页锁在等待进程持有时可被抢占的次数上限。当锁被抢占次数达到本文件指定的阈值(默认为5次)后,将启用“公平锁交接”语义,此时仅在锁可被成功获取时才会唤醒等待进程。


percpu_pagelist_fraction

此参数定义每个内存区(zone)中可分配给每CPU页面列表的页比例上限,具体值按在线CPU数量动态划分。最小值为8(即不允许每区超过1/8的页存储在每CPU页面列表中)。用户可通过指定数值(如100)控制每区分配给每CPU列表的比例(如1/100)。
注意

  • 每CPU页面列表的批量大小不随此值变化,因此分配延迟不受影响。
  • 初始值为0,表示内核根据区低水位和本地在线CPU数自动计算高水位标记。若写入0,则恢复默认行为。

stat_interval

虚拟机统计信息(如/proc/meminfo)的更新时间间隔,默认1秒。


stat_refresh

任何读取或写入(仅root权限)操作会将所有每CPU内存统计信息汇总到全局值,用于生成更精确的报告(例如执行cat /proc/sys/vm/stat_refresh /proc/meminfo)。

副作用
• 检查负值总和(报告中显示为0),若发现负值则返回EINVAL错误并在dmesg中记录警告(已知部分统计偶尔为负,但无实际影响,相关错误已抑制)。


unprivileged_userfaultfd

此标志控制非特权用户使用userfaultfd系统调用的模式:

  • 设置为0:限制非特权用户仅能在用户模式处理页故障。此时,无SYS_CAP_PTRACE权限的用户必须传递UFFD_USER_MODE_ONLY标志才能成功调用。禁止内核模式故障处理可降低某些漏洞利用风险。
  • 设置为1:允许非特权用户无限制使用userfaultfd。
    默认值:0
    补充:可通过/dev/userfaultfd设备替代userfaultfd(2)进行权限控制,详见Userfaultfd文档(https://www.kernel.org/doc/html/latest/admin-guide/mm/userfaultfd.html)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜暝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值