linux - /proc/PID/maps 中的散点图
问题描述
我在 Linux 系统上检查 Python 进程的内存映射,发现了一些非常令人惊讶的东西。通常,当我检查 Python 进程的地图时,它们看起来像这样:
00400000-00401000 r-xp 00000000 fe:01 2904802 python3.9
00600000-00601000 r--p 00000000 fe:01 2904802 python3.9
00601000-00602000 rw-p 00001000 fe:01 2904802 python3.9
00637000-00abe000 rw-p 00000000 00:00 0 [heap]
...
7f67d8565000-7f67d8593000 rw-p 00000000 00:00 0
7f67d8593000-7f67d88ea000 r-xp 00000000 fe:01 2904547 libpython3.9.so.1.0
7f67d88ea000-7f67d8ae9000 ---p 00357000 fe:01 2904547 libpython3.9.so.1.0
7f67d8ae9000-7f67d8aef000 r--p 00356000 fe:01 2904547 libpython3.9.so.1.0
7f67d8aef000-7f67d8b29000 rw-p 0035c000 fe:01 2904547 libpython3.9.so.1.0
7f67d8b29000-7f67d8b4b000 rw-p 00000000 00:00 0
...
7fff72a4f000-7fff72a70000 rw-p 00000000 00:00 0 [stack]
7fff72a7c000-7fff72a80000 r--p 00000000 00:00 0 [vvar]
7fff72a80000-7fff72a82000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
它具有以下结构:
- 与同一二进制/共享对象关联的所有映射都是连续的。
- 可执行文件 (
python3.9
) 的映射首先出现,打开的共享库的映射出现在可执行文件中的映射之后。这是有道理的,因为首先加载可执行文件,然后加载程序按该DT_NEEDED
部分中的原样加载共享对象。
但是我发现的地图是这样的:
00400000-00401000 r-xp 00000000 fd:00 67488961 python3.9
00600000-00601000 r--p 00000000 fd:00 67488961 python3.9
00601000-00602000 rw-p 00001000 fd:00 67488961 python3.9
0067b000-00a58000 rw-p 00000000 00:00 0 [heap]
...
7f7b46014000-7f7b46484000 r--p 0050b000 fd:00 1059871 libpython3.9.so.1.0
7f7b46484000-7f7b46485000 ---p 00000000 00:00 0
7f7b46485000-7f7b46cda000 rw-p 00000000 00:00 0
7f7b46cda000-7f7b46d16000 r--p 00a3d000 fd:00 1059871 libpython3.9.so.1.0
7f7b46d16000-7f7b46d6f000 rw-p 00000000 00:00 0
7f7b46d6f000-7f7b46d92000 r--p 00001000 fd:00 67488961 python3.9
7f7b46d92000-7f7b46d93000 ---p 00000000 00:00 0
7f7b46d93000-7f7b475d3000 rw-p 00000000 00:00 0
...
7f7b5a35d000-7f7b5a827000 r-xp 00000000 fd:00 1059871 libpython3.9.so.1.0
7f7b5a827000-7f7b5aa27000 ---p 004ca000 fd:00 1059871 libpython3.9.so.1.0
7f7b5aa27000-7f7b5aa2c000 r--p 004ca000 fd:00 1059871 libpython3.9.so.1.0
7f7b5aa2c000-7f7b5aa67000 rw-p 004cf000 fd:00 1059871 libpython3.9.so.1.0
7f7b5aa67000-7f7b5aa8b000 rw-p 00000000 00:00 0
...
7fff26f8e000-7fff27020000 rw-p 00000000 00:00 0 [stack]
7fff27102000-7fff27106000 r--p 00000000 00:00 0 [vvar]
7fff27106000-7fff27108000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 /vsyscall]
这有以下奇怪之处:
- 没有与共享对象
python3.9
或libpython3.9.so.1.0
共享对象相关联的连续段块。事实上,这些都是分散的。 - 在共享库中的映射之后可以找到可执行文件的映射:
7f7b46cda000-7f7b46d16000 r--p 00a3d000 fd:00 1059871 libpython3.9.so.1.0
7f7b46d16000-7f7b46d6f000 rw-p 00000000 00:00 0
7f7b46d6f000-7f7b46d92000 r--p 00001000 fd:00 67488961 python3.9
7f7b46d92000-7f7b46d93000 ---p 00000000 00:00 0
你知道什么会导致这种影响,或者在什么情况下会发生这种情况?您知道在几个共享对象之后加载可执行文件的内存映射是如何可能的吗?
注意:这是使用 kernel Kernel 5.13.12-100.fc33.x86_64
。
解决方案
你知道什么会导致这种影响,或者在什么情况下会发生这种情况?
可执行文件可以很简单地mmap
(部分地)它自己。这可以用来检查它自己的符号表(打印崩溃堆栈跟踪所必需的),或者提取一些嵌入式资源。
可执行文件 (python3.9) 的映射首先出现,打开的共享库的映射出现在可执行文件中的映射之后。
这只是偶然的,并且仅适用于非 PIE 可执行文件。
传统x86_64
上,非 PIE 可执行文件链接到在地址加载,并且共享库通常从主堆栈下方开始加载。0x400000
如果您链接一个非 PIE 可执行文件以在 eg 加载0x7ff000000000
,那么它可能会出现在/proc/$pid/maps
after共享库中。
更新:
这里的 python 二进制文件当然不是映射本身,所以这个解释不适用
- 你不可能知道——你几乎肯定没有阅读Python 3.9 中的所有代码以及你加载的每个模块。
- 无需猜测这些
mmap
ed 区域来自何处,您只需查看.
要查看,请在 GDB 下运行您的程序,然后catch syscall mmap
使用where
. 这将允许您查看每个映射的来源。
推荐阅读
- java - 在 Java 8 中更新 XML 文件
- angular - Angular 表单控件别名
- jasper-reports - 如何使用 jasper studio 在列中启用排序和过滤
- mysql - 从备份中替换数据后引擎中不存在表
- node.js - 无法使用 nodemon 运行 Index.js。它显示“干净退出 - 重启前等待更改”
- powerbi - DAX 查询 - 从多个表中过滤条件相似的过滤器
- ios - 如何在 iOS Swift 中更改音频文件比特率(每秒位数)?
- git - Git 同步子文件夹 git
- c# - 如何有效地计算 asp.net 核心中的 HTTP 调用?
- odoo - 根据选择字段更改多对一域