内存检测凭什么"看见"越界?——影子内存的账本逻辑

举报
黄生 发表于 2026/09/17 20:38:29 2026/09/17
【摘要】 系列二 · 第 3 篇上篇末尾留了个问号:msSanitizer 凭什么"看见"了那多出来的 768 字节?答案是它给算子的内存建了一份平行的"账本",专业名字叫影子内存(Shadow Memory)。先说原理。越界也好、未初始化也好,本质都是"某块内存处于不该被访问的状态"。但真实内存里存的只有数据本身,一个字节是 0 还是 1,并不会告诉你"我还没被写过"或者"这里已经越界了"。工具要做...

系列二 · 第 3 篇

上篇末尾留了个问号:msSanitizer 凭什么"看见"了那多出来的 768 字节?答案是它给算子的内存建了一份平行的"账本",专业名字叫影子内存(Shadow Memory)。

先说原理。越界也好、未初始化也好,本质都是"某块内存处于不该被访问的状态"。但真实内存里存的只有数据本身,一个字节是 0 还是 1,并不会告诉你"我还没被写过"或者"这里已经越界了"。工具要做出判断,就得额外记一份状态——这就是影子内存:真实内存的每个字节,在账本里都有一格,专门记录它的状态。

这套思路不是昇腾首创。CPU 圈的 AddressSanitizer 早就这么干,有篇经典论文叫《How to Shadow Every Byte of Memory Used by a Program》(如何给程序用到的每个字节都配上影子),名字说的就是方法本身。msSanitizer 继承了这份巧思,又为昇腾硬件加了点私货。

在 msSanitizer 里,每个影子字节被拆成两部分:低 2 位记录状态,高 6 位记录"这个字节当前归哪个核管"。状态一共四种,正好塞进 2 位:不可访问(NOACCESS),碰了就是越界;可访问但没写过(UNDEFINED),读了就是未初始化;可访问且已写入(DEFINED),正常状态;还有被踩塌的(ERROR),这个下一篇见。

账本怎么用?逻辑朴素得很。写内存时,把对应影子格置成 DEFINED,相当于记一笔"这里来过货了";读内存时,翻一下账本——如果是 UNDEFINED,说明你在读一件从没被写入的东西;如果是 NOACCESS,说明你伸手进了没分配的地盘。越界、未初始化,一次判明。

看到这你可能要问:上百 GB 的 GM 内存,每个字节都配一格账本,光账本就得额外吃掉等量的空间,这谁受得了?这正是工程上的精妙处。真实内存绝大部分是"整片同状态"的——一大块没分配的,一大块刚写完的。所以账本不做成平铺的,而做成多级目录:先按大块建索引,整块状态一致的,一格就记一片;只有真正被用到、状态开始"花"起来的区域,才展开成逐字节的细账。GM 用了三级目录,片上内存两级,大头开销就这么省了下来。

这套账本,是内存检测的地基。越界和未初始化都靠它,但它只回答"某个字节现在什么状态"。还有一类问题——内存的生命周期,什么时候被谁申请、该不该释放、用完没有——得靠另一套台账。那正是下一篇要聊的东西。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。