了解 Linux /proc/pid/maps 或 /proc/self/maps
- 2024-10-09 09:10:00
- admin 原创
- 81
问题描述:
我正在尝试了解嵌入式 Linux 应用程序的内存使用情况。/proc/pid/maps
实用程序/文件似乎是查看详细信息的良好资源。不幸的是,我不了解所有的列和条目。
匿名 inode 0 条目是什么意思?这些似乎是一些较大的内存段。
解决方案 1:
中的每一行/proc/$PID/maps
描述了进程或线程中连续的虚拟内存区域。每行均包含以下字段:
address perms offset dev inode pathname
08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
地址- 这是进程地址空间中区域的起始和结束地址
权限- 描述如何访问区域中的页面。有四种不同的权限:读取、写入、执行和共享。如果禁用读取/写入/执行,则将
-
显示 而不是r
/w
/x
。如果区域不是共享的,则它是私有的,因此p
将显示 而不是s
。如果进程试图以不允许的方式访问内存,则会产生分段错误。可以使用系统调用更改权限mprotect
。偏移量- 如果区域是从文件映射的(使用
mmap
),则这是映射开始处的文件中的偏移量。如果内存不是从文件映射的,则它只是 0。设备- 如果该区域是从文件映射的,则这是文件所在的主设备号和次设备号(十六进制)。
inode — 如果该区域是从文件映射的,则这就是文件编号。
pathname - 如果区域是从文件映射的,则这是文件的名称。对于匿名映射区域,此字段为空。还有一些特殊区域,其名称如
[heap]
、[stack]
或[vdso]
。[vdso]
代表虚拟动态共享对象。系统调用使用它来切换到内核模式。这里有一篇关于它的好文章:“什么是 linux-gate.so.1?”
您可能会注意到很多匿名区域。这些区域通常由任何文件创建mmap
,但未附加到任何文件。它们用于很多杂项,例如共享内存或未在堆上分配的缓冲区。例如,我认为 pthread 库使用匿名映射区域作为新线程的堆栈。
解决方案 2:
请查看: http: //man7.org/linux/man-pages/man5/proc.5.html
address perms offset dev inode pathname
00400000-00452000 r-xp 00000000 08:02 173521 /usr/bin/dbus-daemon
地址字段是映射所占用的进程中的地址空间。
perms 字段是一组权限:
r = read
w = write
x = execute
s = shared
p = private (copy on write)
偏移量字段是文件/其他内容的偏移量;
dev 是设备(major:minor);
inode 是该设备上的 inode。0 表示没有 inode 与内存区域相关联,就像 BSS(未初始化数据)的情况一样。
路径名字段通常是支持映射的文件。对于 ELF 文件,您可以通过查看 ELF 程序头中的偏移量字段 (readelf -l) 轻松地与偏移量字段进行协调。
在 Linux 2.0 下,没有提供路径名的字段。
解决方案 3:
虽然问题中特别提到了嵌入式系统,但标题只提到了proc/<pid>/maps
,这对于理解“普通”程序也非常有用。在这个更广泛的背景下,重要的是要意识到分配的内存malloc()
可能最终位于堆中或任意数量的匿名内存段中。因此,大块匿名内存很可能来自malloc()
。
更准确地说/proc/<pid>/maps
,[heap]
是分配给静态变量的内存(称为BSS 段)与称为“程序中断”的地址之间的连续区域(见下图)。最初,此区域为空,没有堆。malloc()
调用时,它可以通过请求内核(通过brk()
系统调用)移动程序中断来创建/扩展堆。同样,free()
如果程序中断附近的所有地址都不再使用,则可以缩小堆。
但是,移动程序中断并不是malloc()
为自己腾出更多空间的唯一方法。它还可以通过mmap()
系统调用要求内核在堆栈和堆之间的某个位置保留一块空间(见下图)。以这种方式分配的内存显示为/proc/<pid>/maps
问题中提到的“匿名 inode 0 条目”。
图片来源
值得对mmap()
系统调用进行一些详细说明。mmap()
可以创建四种类型的内存映射,每种类型的内存映射的用途都大不相同。首先,内存可以与某个文件的内容绑定,也可以不绑定。后者称为“匿名”映射。其次,内存可以是“私有”的,也可以是“共享”的。私有意味着一个进程所做的更改对其他进程不可见;这通常以一种称为“写时复制”的惰性且高效的方式实现。共享意味着每个进程都可以访问相同的底层物理内存。以下是我所知道的每种内存映射的用途:
私有文件:可执行文件、动态库、大型数据结构的有效副本
私有匿名映射:
malloc()
动态库的 BSS 段、线程的堆栈空间共享文件:在不相关的进程之间共享内存
共享匿名映射:相关进程之间共享内存
回到/proc/<pid>/maps
,您可以通过查看“pathname”和“perms”列来确定每行描述的是哪种内存映射。(这些列名来自内核文档)。对于文件映射,“pathname”列将保存被映射文件的实际路径。对于匿名映射,“pathname”列将为空。还有一些特殊路径名,如[heap]
和[stack]
。对于私有和共享映射,“perms”列将分别包含p
或s
标志。
当前用于小分配和大分配的实现malloc()
。brk()
在mmap()
堆上分配少量内存是有意义的,因为通常可以找到必要的空间而不必进行昂贵的系统调用(例如,通过重用以前释放的空间)。但是,大分配存在永远不会被释放回操作系统的风险。考虑一下如果您在堆上进行大分配,然后进行一堆小分配会发生什么。即使在释放大分配之后,程序中断也不能移回,直到所有小分配也被释放。这个简单的例子假设分配按顺序进入堆,这是一种幼稚的方法,但它说明了堆如何使将内存释放回操作系统变得更加困难。
以下是相关部分man malloc
:
通常,malloc() 从堆中分配内存,并使用 sbrk(2) 根据需要调整堆的大小。当分配大于 MMAP_THRESHOLD 字节的内存块时,glibc malloc() 实现会使用 mmap(2) 将内存分配为私有匿名映射。MMAP_THRESHOLD 默认为 128 kB,但可以使用 mallopt(3) 进行调整。在 Linux 4.7 之前,使用 mmap(2) 执行的分配不受 RLIMIT_DATA 资源限制的影响;自 Linux 4.7 起,此限制也适用于使用 mmap(2) 执行的分配。
总之,如果您的程序使用malloc()
,那么malloc()
很可能对许多映射到虚拟内存并由报告的大型匿名段负责/proc/<pid>/maps
。
买者自慎:我在这里写的几乎所有内容都是我今天才学到的,所以请谨慎对待。话虽如此,以下是我发现对理解所有这些非常有帮助的资源链接:
虚拟内存的出色介绍
/proc/<pid>/maps 的内核文档
这是整个文件系统文档的链接
/proc
,不幸的是,我认为没有办法直接链接到相关部分。但如果您搜索“/proc/PID/maps”,您应该能够找到正确的位置。
堆的精确定义
为什么调用不会
malloc()
创建堆?优缺点
brk()
mmap()
如何工作
malloc()
free()
解决方案 4:
内存映射不仅用于将文件映射到内存中,也是从内核请求 RAM 的工具。这些是 inode 0 条目 - 您的堆栈、堆、bss 段等
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件