Skip to content

Commit 8819b60

Browse files
tttturtle-russJonathan Corbet
authored andcommitted
docs/zh_CN: Add dev-tools/kmemleak Chinese translation
Translate dev-tools/kmemleak.rst into Chinese and add it into zh_CN/dev-tools/index.rst. Signed-off-by: Haoyang Liu <tttturtleruss@hust.edu.cn> Reviewed-by: Yanteng Si <siyanteng@loongson.cn> Signed-off-by: Jonathan Corbet <corbet@lwn.net> Link: https://lore.kernel.org/r/20240406083643.5056-1-tttturtleruss@hust.edu.cn
1 parent 9e66f74 commit 8819b60

2 files changed

Lines changed: 230 additions & 1 deletion

File tree

Documentation/translations/zh_CN/dev-tools/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ Documentation/translations/zh_CN/dev-tools/testing-overview.rst
2323
gcov
2424
kasan
2525
ubsan
26+
kmemleak
2627
gdb-kernel-debugging
2728

2829
Todolist:
2930

3031
- coccinelle
3132
- kcov
32-
- kmemleak
3333
- kcsan
3434
- kfence
3535
- kgdb
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
.. SPDX-License-Identifier: GPL-2.0
2+
3+
.. include:: ../disclaimer-zh_CN.rst
4+
5+
:Original: Documentation/dev-tools/kmemleak.rst
6+
:Translator: 刘浩阳 Haoyang Liu <tttturtleruss@hust.edu.cn>
7+
8+
内核内存泄露检测器
9+
==================
10+
11+
Kmemleak 提供了一个类似 `可追踪的垃圾收集器 <https://en.wikipedia.org/wiki/Tra
12+
cing_garbage_collection>`_ 的方法来检测可能的内核内存泄漏,不同的是孤立对象不会
13+
被释放,而是仅通过 /sys/kernel/debug/kmemleak 报告。Valgrind 工具
14+
(``memcheck --leak-check``)使用了一种相似的方法来检测用户空间应用中的内存泄
15+
露。
16+
17+
用法
18+
----
19+
20+
"Kernel hacking" 中的 CONFIG_DEBUG_KMEMLEAK 必须被启用。一个内核线程每10分钟
21+
(默认情况下)扫描一次内存,并且打印出新发现的未被引用的对象个数。
22+
如果 ``debugfs`` 没有挂载,则执行::
23+
24+
# mount -t debugfs nodev /sys/kernel/debug/
25+
26+
显示所有扫描出的可能的内存泄漏的细节信息::
27+
28+
# cat /sys/kernel/debug/kmemleak
29+
30+
启动一次中等程度的内存扫描::
31+
32+
# echo scan > /sys/kernel/debug/kmemleak
33+
34+
清空当前所有可能的内存泄露列表::
35+
36+
# echo clear > /sys/kernel/debug/kmemleak
37+
38+
当再次读取 ``/sys/kernel/debug/kmemleak`` 文件时,将会输出自上次扫描以来检测到的
39+
新的内存泄露。
40+
41+
注意,孤立目标是通过被分配时间来排序的,列表开始的对象可能会导致后续的对象都被
42+
识别为孤立对象。
43+
44+
可以通过写入 ``/sys/kernel/debug/kmemleak`` 文件在运行时修改内存扫描参数。下面是
45+
支持的参数:
46+
47+
48+
* off
49+
禁用 kmemleak(不可逆)
50+
* stack=on
51+
开启任务栈扫描(默认)
52+
* stack=off
53+
禁用任务栈扫描
54+
* scan=on
55+
开启自动内存扫描线程(默认)
56+
* scan=off
57+
关闭自动内存扫描线程
58+
* scan=<secs>;
59+
设定自动内存扫描间隔,以秒为单位(默认值为 600,设置为 0 表示停
60+
止自动扫描)
61+
* scan
62+
触发一次内存扫描
63+
* clear
64+
通过标记所有当前已报告的未被引用对象为灰,从而清空当前可能的内存泄露列
65+
表;如果 kmemleak 被禁用,则释放所有 kmemleak 对象,。
66+
* dump=<addr>
67+
输出存储在 <addr> 中的对象信息
68+
69+
可以通过在内核命令行中传递 ``kmemleak=off`` 参数从而在启动时禁用 Kmemleak。
70+
71+
在 kmemleak 初始化之前就可能会有内存分配或释放,这些操作被存储在一个早期日志缓
72+
冲区中。缓冲区的大小通过 CONFIG_DEBUG_KMEMLEAK_MEM_POOL_SIZE 选项配置。
73+
74+
如果 CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF 被启用,则 kmemleak 默认被禁用。在内核命
75+
令行中传递 ``kmemleak=on`` 参数来开启这个功能。
76+
77+
如果出现 "Error while writing to stdout" 或 "write_loop: Invalid argument" 这样
78+
的错误,请确认 kmemleak 被正确启用。
79+
80+
基础算法
81+
--------
82+
83+
通过 :c:func:`kmalloc`, :c:func:`vmalloc`, :c:func:`kmem_cache_alloc` 以及同类
84+
函数均被跟踪,指针,包括一些额外的信息如大小和栈追踪等,都被存储在红黑树中。
85+
对应的释放函数调用也被追踪,并从 kmemleak 数据结构中移除相应指针。
86+
87+
对于一个已分配的内存块,如果通过扫描内存(包括保存寄存器)没有发现任何指针指向
88+
它的起始地址或者其中的任何位置,则认为这块内存是孤立的。这意味着内核无法将该内
89+
存块的地址传递给一个释放内存函数,这块内存便被认为泄露了。
90+
91+
扫描算法步骤:
92+
93+
1. 标记所有对象为白色(最后剩下的白色对象被认为是孤立的)
94+
2. 从数据节和栈开始扫描内存,检测每个值是否是红黑树中存储的地址。如果一个指向
95+
白色对象的指针被检测到,则将该对象标记为灰色。
96+
3. 扫描灰色对象引用的其他对象(有些白色对象可能会变为灰色并被添加到灰名单末尾
97+
)直到灰名单为空。
98+
4. 剩余的白色对象就被认为是孤立的并通过 /sys/kernel/debug/kmemleak 报告。
99+
100+
有些指向已分配的内存块的指针存储在内核内部的数据结构中,它们不能被检测为孤立。
101+
为了避免这种情况,kmemleak 也存储了指向需要被查找的内存块范围内的任意地址的地址
102+
数量,如此一来这些内存便不会被认为泄露。一个例子是 __vmalloc()。
103+
104+
用 kmemleak 测试特定部分
105+
------------------------
106+
107+
在初始化启动阶段 /sys/kernel/debug/kmemleak 的输出可能会很多,这也可能是你在开发
108+
时编写的漏洞百出的代码导致的。为了解决这种情况你可以使用 'clear' 命令来清除
109+
/sys/kernel/debug/kmemleak 输出的所有的未引用对象。在执行 'clear' 后执行 'scan'
110+
可以发现新的未引用对象,这将会有利你测试代码的特定部分。
111+
112+
为了用一个空的 kmemleak 测试一个特定部分,执行::
113+
114+
# echo clear > /sys/kernel/debug/kmemleak
115+
... 测试你的内核或者模块 ...
116+
# echo scan > /sys/kernel/debug/kmemleak
117+
118+
然后像平常一样获得报告::
119+
120+
# cat /sys/kernel/debug/kmemleak
121+
122+
释放 kmemleak 内核对象
123+
----------------------
124+
125+
为了允许访问先前发现的内存泄露,当用户禁用或发生致命错误导致 kmemleak
126+
被禁用时,内核中的 kmemleak 对象不会被释放。这些对象可能会占用很大
127+
一部分物理内存。
128+
129+
在这种情况下,你可以用如下命令回收这些内存::
130+
131+
# echo clear > /sys/kernel/debug/kmemleak
132+
133+
Kmemleak API
134+
------------
135+
136+
在 include/linux/kmemleak.h 头文件中查看函数原型:
137+
138+
- ``kmemleak_init`` - 初始化 kmemleak
139+
- ``kmemleak_alloc`` - 通知一个内存块的分配
140+
- ``kmemleak_alloc_percpu`` - 通知一个 percpu 类型的内存分配
141+
- ``kmemleak_vmalloc`` - 通知一个使用 vmalloc() 的内存分配
142+
- ``kmemleak_free`` - 通知一个内存块的释放
143+
- ``kmemleak_free_part`` - 通知一个部分的内存释放
144+
- ``kmemleak_free_percpu`` - 通知一个 percpu 类型的内存释放
145+
- ``kmemleak_update_trace`` - 更新分配对象过程的栈追踪
146+
- ``kmemleak_not_leak`` - 标记一个对象内存为未泄露的
147+
- ``kmemleak_ignore`` - 不要扫描或报告某个对象未泄露的
148+
- ``kmemleak_scan_area`` - 在内存块中添加扫描区域
149+
- ``kmemleak_no_scan`` - 不扫描某个内存块
150+
- ``kmemleak_erase`` - 在指针变量中移除某个旧的值
151+
- ``kmemleak_alloc_recursive`` - 和 kmemleak_alloc 效果相同但会检查是否有递归的
152+
内存分配
153+
- ``kmemleak_free_recursive`` - 和 kmemleak_free 效果相同但会检查是否有递归的
154+
内存释放
155+
156+
下列函数使用一个物理地址作为对象指针并且只在地址有一个 lowmem 映射时做出相应的
157+
行为:
158+
159+
- ``kmemleak_alloc_phys``
160+
- ``kmemleak_free_part_phys``
161+
- ``kmemleak_ignore_phys``
162+
163+
解决假阳性/假阴性
164+
-----------------
165+
166+
假阴性是指由于在内存扫描中有值指向该对象导致 kmemleak 没有报告的实际存在的内存
167+
泄露(孤立对象)。为了减少假阴性的出现次数,kmemleak 提供了 kmemleak_ignore,
168+
kmemleak_scan_area,kmemleak_no_scan 和 kmemleak_erase 函数(见上)。
169+
任务栈也会增加假阴性的数量并且默认不开启对它们的扫描。
170+
171+
假阳性是对象被误报为内存泄露(孤立对象)。对于已知未泄露的对象,kmemleak
172+
提供了 kmemleak_not_leak 函数。同时 kmemleak_ignore 可以用于标记已知不包含任何
173+
其他指针的内存块,标记后该内存块不会再被扫描。
174+
175+
一些被报告的泄露仅仅是暂时的,尤其是在 SMP(对称多处理)系统中,因为其指针
176+
暂存在 CPU 寄存器或栈中。Kmemleak 定义了 MSECS_MIN_AGE(默认值为 1000)
177+
来表示一个被报告为内存泄露的对象的最小存活时间。
178+
179+
限制和缺点
180+
----------
181+
182+
主要的缺点是内存分配和释放的性能下降。为了避免其他的损失,只有当
183+
/sys/kernel/debug/kmemleak 文件被读取时才会进行内存扫描。无论如何,这个工具是出于
184+
调试的目标,性能表现可能不是最重要的。
185+
186+
为了保持算法简单,kmemleak 寻找指向某个内存块范围中的任何值。这可能会引发假阴性
187+
现象的出现。但是,最后一个真正的内存泄露也会变得明显。
188+
189+
非指针值的数据是假阴性的另一个来源。在将来的版本中,kmemleak 仅仅会扫
190+
描已分配结构体中的指针成员。这个特性会解决上述很多的假阴性情况。
191+
192+
Kmemleak 会报告假阳性。这可能发生在某些被分配的内存块不需要被释放的情况下
193+
(某些 init_call 函数中),指针的计算是通过其他方法而不是常规的 container_of 宏
194+
或是指针被存储在 kmemleak 没有扫描的地方。
195+
196+
页分配和 ioremap 不会被追踪。
197+
198+
使用 kmemleak-test 测试
199+
-----------------------
200+
201+
为了检测是否成功启用了 kmemleak,你可以使用一个故意制造内存泄露的模块
202+
kmemleak-test。设置 CONFIG_SAMPLE_KMEMLEAK 为模块(不能作为内建模块使用)
203+
并且启动启用了 kmemleak 的内核。加载模块并执行一次扫描::
204+
205+
# modprobe kmemleak-test
206+
# echo scan > /sys/kernel/debug/kmemleak
207+
208+
注意你可能无法立刻或在第一次扫描后得到结果。当 kmemleak 得到结果,将会输出日
209+
志 ``kmemleak: <count of leaks> new suspected memory leaks`` 。然后通过读取文件
210+
获取信息::
211+
212+
# cat /sys/kernel/debug/kmemleak
213+
unreferenced object 0xffff89862ca702e8 (size 32):
214+
comm "modprobe", pid 2088, jiffies 4294680594 (age 375.486s)
215+
hex dump (first 32 bytes):
216+
6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk
217+
6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b a5 kkkkkkkkkkkkkkk.
218+
backtrace:
219+
[<00000000e0a73ec7>] 0xffffffffc01d2036
220+
[<000000000c5d2a46>] do_one_initcall+0x41/0x1df
221+
[<0000000046db7e0a>] do_init_module+0x55/0x200
222+
[<00000000542b9814>] load_module+0x203c/0x2480
223+
[<00000000c2850256>] __do_sys_finit_module+0xba/0xe0
224+
[<000000006564e7ef>] do_syscall_64+0x43/0x110
225+
[<000000007c873fa6>] entry_SYSCALL_64_after_hwframe+0x44/0xa9
226+
...
227+
228+
用 ``rmmod kmemleak_test`` 移除模块时也会触发
229+
kmemleak 的结果输出。

0 commit comments

Comments
 (0)