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 个块合并成一行,也可能因为状态或其他拆分条件分成多行。

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 等支持的列拆分。拆分选项改变的是“如何分行”,不会改变底层的单块大小。

| 模式 | 观察粒度 | 适合的问题 |
|---|---|---|
| 默认 | 按可合并属性形成范围 | 快速看地址段和状态分布 |
--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,需要行级明细时使用显式列清单,边界会更清楚。
我会怎样快速判断一行大小
- 先看摘要的
Memory block size,确定单块粒度。 - 再看该行的
BLOCK,判断它是一个块还是连续块范围。 - 用块数量乘单块大小,与该行
SIZE对照。 - 用
RANGE的起止地址计算“末 - 首 + 1”,确认它是地址区间而不是累计总量。 - 最后再把各行按状态合并,和摘要中的 online/offline 总量比较。
常见问题
为什么 RANGE 看起来很大,SIZE 却不是末地址那么大?
因为 RANGE 是起止地址表示法,SIZE 是区间长度。长度应计算为结束地址减起始地址再加 1,不能直接读取末地址的数值。
默认输出的一行就是一个 memory block 吗?
不一定。默认输出可能把连续且属性相同的多个块合并成一个范围;用 --all 才是逐块列出。
为什么某一行 SIZE 加起来和 Total online memory 对不上?
先确认是否把 offline 行也算进去了,再确认是否只截取了部分输出或改变了拆分/合并条件。摘要是按所有块的状态累计,表格行则受显示策略影响。
把 lsmem 看成“单块、范围、汇总”三层数据,就不会再把地址末值、范围大小和在线总量混在一起。排查时固定使用 --bytes、显式列清单,并在需要逐块证据时加上 --all,通常就足够稳定。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
329 收藏
-
369 收藏
-
409 收藏
-
107 收藏
-
251 收藏
-
269 收藏
-
209 收藏
-
332 收藏
-
481 收藏
-
373 收藏
-
279 收藏
-
183 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习