登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

Linux lsmem 的汇总大小与单块地址范围有什么区别

来源:17golang原创

时间:2026-10-30 20:11:00 296浏览 收藏

我第一次用 lsmem 看内存热插拔信息时,最容易误读的是把摘要里的“总大小”和表格里的地址范围当成同一个数。其实它们处在三层口径:Memory block size 说的是单个内存块的固定粒度,表格中的 RANGE 和 SIZE 说的是一行被合并后的范围,而 Total online memory 与 Total offline memory 是对所有单块状态的汇总。

官方文档:https://github.com/util-linux/util-linux/blob/master/sys-utils/lsmem.1.adoc

记住一个最实用的判断:RANGE 是“从哪个物理地址到哪个物理地址”,SIZE 是“这一行覆盖了多少字节”,摘要中的 online/offline 则是“所有内存块按状态加起来有多少”。地址末值不是大小,单块大小也不是总量。

先把汇总区和行级字段分开

lsmem 读取的是 Linux 内存块表示。默认摘要通常包含三项:Memory block size、Total online memory 和 Total offline memory。前者是每个 memory block 的固定大小,后两项则按每个块的 online/offline 状态累加。

字段统计对象怎样使用
Memory block size单个 memory block判断一个 BLOCK 对应多少字节
Total online memory状态为 online 的全部块看当前在线内存总量
Total offline memory状态为 offline 的全部块看尚未在线的内存总量
RANGE / SIZE表格中的一个范围行定位地址区间和该区间的大小

例如,一个系统的单块大小是 128 MiB,摘要中的在线总量是 1 GiB,这并不意味着某一行的 RANGE 必须覆盖 1 GiB。表格可能把连续、属性相同的 8 个块合并成一行,也可能因为状态或其他拆分条件分成多行。

Linux lsmem 单块大小、连续范围、RANGE、SIZE 和在线总量的层级说明图
图1:lsmem 从单块到范围再到汇总的统计层级说明图,不是终端截图或运行证据。

RANGE 是地址边界,SIZE 才是范围大小

官方列定义把 RANGE 解释为内存范围的起始和结束物理地址,把 SIZE 解释为该内存范围的大小。可以用下面的关系快速检查一行是否读对:

范围大小 = 结束地址 - 起始地址 + 1
范围大小 = BLOCK 数量 × Memory block size

第二个等式成立的前提是这行表示连续的 memory block。假设单块大小为 B,这一行从第 i 个块开始、连续覆盖 n 个块,那么起始地址是 i × B,结束地址是 i × B + n × B - 1,而 SIZE 是 n × B。因此,看到类似 0x0000000040000000-0x000000007fffffff 时,不能直接把末地址当作大小;应该计算两端差值再加 1。

BLOCK 列是很好的交叉参照:单块时显示一个块号,合并后显示块号范围。它回答“这行由哪些块组成”,RANGE 回答“这些块对应哪段物理地址”,SIZE 回答“这段地址一共多大”。

# 显式选择脚本需要的列,并用字节避免 MiB/GiB 舍入造成误读
lsmem --bytes --output=RANGE,SIZE,STATE,BLOCK

# 只看摘要,快速得到单块大小和在线、离线总量
lsmem --bytes --summary=only

这里的命令只是读取方式示例。第一条适合做行级关联,第二条适合看总体盘点;两条输出的“大小”不能互相替代。

默认合并为什么会改变行数

我在排查热插拔状态时,曾经把默认输出的两行当成系统只有两个物理块。实际上,lsmem 默认会在当前 memory block 与前一个块的输出属性满足合并条件时,把它们收进同一范围。于是,一行可能对应一个块,也可能对应连续的多个块。

如果需要逐块观察,可以使用 --all;如果只希望按某个属性切开范围,可以使用 --split=STATE,也可以按 NODE、ZONES 等支持的列拆分。拆分选项改变的是“如何分行”,不会改变底层的单块大小。

lsmem 默认合并、--all 和 --split=STATE 对同一组内存块的显示粒度对照图
图2:同一组 memory block 在不同 lsmem 显示模式下的静态对照图,不是实际输出截图。
模式观察粒度适合的问题
默认按可合并属性形成范围快速看地址段和状态分布
--all每个 memory block 一行定位某个块的具体状态
--split=STATE状态变化处切段把 online 与 offline 区间分开阅读

还要注意,拆分策略如果强制忽略某些属性差异,某些列可能只能代表合并范围中的一个状态。官方文档特别提醒,合并可移除与不可移除块时,整行的 REMOVABLE 可能显示为不可移除。因此,想核对单块属性时,用 --all 比默认合并更稳妥。

脚本化读取时把单位和摘要边界固定下来

默认输出适合人读,不适合直接当作稳定接口。官方文档建议脚本显式指定 --output 和列清单;同时用 --bytes 输出整数型字节值,避免人类可读单位在不同数据量下发生单位切换。

# 让脚本只读取稳定列;awk 这里只展示字段传递,不把结果当成线上实测值
lsmem --bytes --noheadings --output=RANGE,SIZE,STATE,BLOCK |
  awk '{ print "range=" $1, "size_bytes=" $2, "state=" $3, "block=" $4 }'

# 需要完整逐块关系时再加 --all,避免默认合并隐藏块边界
lsmem --all --bytes --noheadings --output=RANGE,SIZE,STATE,BLOCK

如果改用 --json、--pairs 或 --raw,摘要输出会被抑制;这意味着脚本不能一边期待 JSON 表格,一边再从同一次输出里读取 Total online memory。需要摘要时单独调用 --summary=only,需要行级明细时使用显式列清单,边界会更清楚。

我会怎样快速判断一行大小

  1. 先看摘要的 Memory block size,确定单块粒度。
  2. 再看该行的 BLOCK,判断它是一个块还是连续块范围。
  3. 用块数量乘单块大小,与该行 SIZE 对照。
  4. 用 RANGE 的起止地址计算“末 - 首 + 1”,确认它是地址区间而不是累计总量。
  5. 最后再把各行按状态合并,和摘要中的 online/offline 总量比较。

常见问题

为什么 RANGE 看起来很大,SIZE 却不是末地址那么大?

因为 RANGE 是起止地址表示法,SIZE 是区间长度。长度应计算为结束地址减起始地址再加 1,不能直接读取末地址的数值。

默认输出的一行就是一个 memory block 吗?

不一定。默认输出可能把连续且属性相同的多个块合并成一个范围;用 --all 才是逐块列出。

为什么某一行 SIZE 加起来和 Total online memory 对不上?

先确认是否把 offline 行也算进去了,再确认是否只截取了部分输出或改变了拆分/合并条件。摘要是按所有块的状态累计,表格行则受显示策略影响。

把 lsmem 看成“单块、范围、汇总”三层数据,就不会再把地址末值、范围大小和在线总量混在一起。排查时固定使用 --bytes、显式列清单,并在需要逐块证据时加上 --all,通常就足够稳定。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>