【问题标题】:Coding pmap in Solaris在 Solaris 中编写 pmap
【发布时间】:2011-12-20 14:34:01
【问题描述】:

我目前正在尝试编写自己的程序来反映 pmap 命令,特别是在 Solaris 9 上。我在解析库的名称和路径时遇到了问题。来自 bash shell 的 Solaris 命令输出如下所示:

bash-2.05# pmap $$
2427:   bash
00010000     496K r-x--  /usr/bin/bash
0009A000      80K rwx--  /usr/bin/bash
000AE000     120K rwx--    [ heap ]
FF100000     688K r-x--  /usr/lib/libc.so.1
FF1BC000      24K rwx--  /usr/lib/libc.so.1
FF1C2000       8K rwx--  /usr/lib/libc.so.1
FF200000     568K r-x--  /usr/lib/libnsl.so.1
FF29E000      32K rwx--  /usr/lib/libnsl.so.1
FF2A6000      32K rwx--  /usr/lib/libnsl.so.1
FF2F0000       8K rwx--    [ anon ]
FF300000      16K r-x--  /usr/lib/libmp.so.2
FF314000       8K rwx--  /usr/lib/libmp.so.2
FF320000       8K r-x--  /usr/platform/sun4u-us3/lib/libc_psr.so.1
FF330000      40K r-x--  /usr/lib/libsocket.so.1
FF34A000       8K rwx--  /usr/lib/libsocket.so.1
FF350000     168K r-x--  /usr/lib/libcurses.so.1
FF38A000      32K rwx--  /usr/lib/libcurses.so.1
FF392000       8K rwx--  /usr/lib/libcurses.so.1
FF3A0000       8K r-x--  /usr/lib/libdl.so.1
FF3B0000       8K rwx--    [ anon ]
FF3C0000     152K r-x--  /usr/lib/ld.so.1
FF3F6000       8K rwx--  /usr/lib/ld.so.1
FFBFC000      16K rw---    [ stack ]
 total      2536K

我可以通过阅读 /proc/$$/map 来复制程序的基本功能,但剩下的就是弄清楚如何解析库名称,如右图所示。 /proc/$$/map 仅给出 /proc/$$/object 中文件的名称,它们只是通用名称。在 Solaris 10(我有一个盒子)上,我似乎能够使用 /proc/$$/path,其中包含符号链接,但我正在处理的盒子没有这些。有没有人对如何获取这些库名称有任何直接的想法?当我捆绑程序时,它似乎打开 /proc/$$/as 并查看内存并以某种方式找到它们,但我还无法弄清楚它在哪里寻找或为什么。

【问题讨论】:

  • 如果你 google 'c pmap',你会发现一个人(旧的)重新设计的 pmap(对于 solaris)。时间不长。也许你可以在那里找到你出错的地方(如果代码仍然有效,考虑到它的年龄)。
  • 我找到的是 Linux 的 pmap 实现,不幸的是完全不同,因为 Linux /proc/$$/maps 是一个明文文件,其中包含我需要的所有内容。你看到的还有什么我错过的吗?
  • 我又检查了一遍。你说得对。我没有仔细阅读。道歉。不幸的是,我在 Linux 中工作,所以不知道这两种方言的实现有何不同。
  • 我不知道它到底是在哪个级别实现的,但是 hub.opensolaris.org/bin/view/Main/get 祝你好运
  • 不幸的是,OpenSolaris 版本的工作方式似乎有点不同,至少据我所知。它尝试使用 /proc/$$/path 文件,该文件在我的 Solaris 9 上不存在,否则默认为设备/inode 名称。再次感谢您环顾四周! :)

标签: c solaris pmap


【解决方案1】:

pmap 的 Solaris 实现实际上可以通过 OpenSolarispmap.c 以源代码形式获得。但这是非常复杂的东西;基础更容易实现。

Solaris 有 三个 /proc 地图句柄:

  1. /proc/<PID>/map 包含反映“通常”工作集大小的 struct prmap 数据
  2. /proc/<PID>/rmap 还包含 struct prmap 数据,但用于虚拟集大小 (VSZ),即反映映射的 VA 范围,即使映射未提交。
  3. /proc/<PID>/xmap 包含 struct prxmap 数据,它实际上是对地址空间进行搜索以识别内存驻留区域。

pmap 实用程序会在您不传递任何参数 (map)、-r (rmap) 或 -x (xmap) 时检查这些,但正如您发现的那样不仅仅是打开/读取地图 proc 文件。这主要是导致源代码难以解析的原因。

不过,您可以通过以下代码构造一个简单的 Solaris [rx]map(当然,如果您访问 xmap,则使用 struct prxmap 而不是 struct prmap)解析器:

char mappath[MAXPATHLEN];
sprintf(mappath, "/proc/%d/map", getpid());
int fd = open(mappath, O_RDONLY);
size_t nread;
size_t mapsz = (1 << 20);                 /* start at 1MB */
struct prmap *cur*mapbuf = malloc(mapsz);

while ((nread = pread(fd, mapbuf, mapsz, 0)) == mapsz) {
    free(mapbuf);
    mapsz *= 2;
    mapbuf = malloc(mapsz);
}
for (cur = mapbuf; nread; cur++, nread -= sizeof(*mapbuf))
    prettyprint(cur);
free(mapbuf);

一些提示:

  • 不要尝试mmap() map 文件,这不起作用(procfs 不允许这样做)
  • 不要尝试顺序读取部分内容;以上(从开始重新读取整个缓冲区到调整大小的缓冲区)比执行read()的连续序列更快。始终从头开始阅读(在示例中使用pread,或在任何read 之前使用lseek(..., 0, SEEK_SET))。

享受实验!

编辑:

既然您已经下定决心要找到映射背后的路径名,那是非常困难的任务之一。 pmap 本身并不处理它,而是使用 libproc 的工具来解析这些名称,正如您在 truss 中发现的那样,它可能会在进程地址空间中搜索以提取这些名称,但也使用其他一些技术。它本质上是libproc/Psymtab.c 中的“iter”函数执行此操作。 Solaris 9 和更高版本之间也存在差异……不记得了,太久以前了……

【讨论】:

  • 感谢您的详尽回答,以及关于不执行一系列顺序 read() 调用更快的提示,因为这就是我正在做的!
  • 对于小型映射(几百个条目),开销是不可测量的,但是如果您有数十万个映射,例如一个巨大的 64 位 JVM 实例,并且可能还有一些动态,那么它会产生很大的不同。
  • 感谢您的精彩回答。
  • 有一种获取路径名的方法,但它仅适用于系统库。您可以查看 /proc//map 文件中的 prmap_t 结构,并从 /proc//objects 获取目标文件的名称。然后可以将那里的对象解析为 elf 文件,并从 /usr/lib 或 /lib 获取每个文件的库 soname,即系统 .so 文件的名称。但是,这不适用于用户的库。这里只能使用libproc,但它的接口应该是关闭的,所以你要使用nm工具,希望接口和opensol中描述的一样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-15
  • 1970-01-01
  • 1970-01-01
  • 2019-11-03
  • 1970-01-01
  • 2011-09-02
  • 2019-10-30
相关资源
最近更新 更多