获取 /proc/pid/smaps 的一致快照的正确方法是什么?



我正在尝试从C++二进制文件中的进程/proc/<pid>/smaps解析PSS值。

根据这个SO答案,例如天真地读取/proc/<pid>/smaps文件ifstream::getLine()将导致数据集不一致。建议的解决方案是使用read()系统调用一次性读取整个数据,如下所示:

#include <unistd.h>
#include <fcntl.h>
...
char rawData[102400];
int file = open("/proc/12345/smaps", O_RDONLY, 0);
auto bytesRead = read(file, rawData, 102400); // this returns 3722 instead of expected ~64k
close(file);
std::cout << bytesRead << std::endl; 
// do some parsing here after null-terminating the buffer
...

我现在的问题是,尽管我使用了 100kB 的缓冲区,但只返回了 3722 字节。查看使用 strace 解析文件时cat的作用,我发现它使用多个调用来read()(每次读取时也获得大约 3k 字节(,直到read()返回 0 - 如read()的文档中所述:

...
read(3, "7fa8db3d7000-7fa8db3d8000 r--p 0"..., 131072) = 3588
write(1, "7fa8db3d7000-7fa8db3d8000 r--p 0"..., 3588) = 3588
read(3, "7fa8db3df000-7fa8db3e0000 r--p 0"..., 131072) = 3632
write(1, "7fa8db3df000-7fa8db3e0000 r--p 0"..., 3632) = 3632
read(3, "7fa8db3e8000-7fa8db3ed000 r--s 0"..., 131072) = 3603
write(1, "7fa8db3e8000-7fa8db3ed000 r--s 0"..., 3603) = 3603
read(3, "7fa8db41d000-7fa8db425000 r--p 0"..., 131072) = 3445
write(1, "7fa8db41d000-7fa8db425000 r--p 0"..., 3445) = 3445
read(3, "7fff05467000-7fff05496000 rw-p 0"..., 131072) = 2725
write(1, "7fff05467000-7fff05496000 rw-p 0"..., 2725) = 2725
read(3, "", 131072)                     = 0
munmap(0x7f8d29ad4000, 139264)          = 0
close(3)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

但是,根据上面链接的SO答案,这不应该产生不一致的数据吗?

我还在这里找到了一些关于proc的信息,这些信息似乎支持之前的SO答案:

要看到精确的 快照,您可以看到/proc//smaps 文件和扫描页表。

然后在文本的后面它说:

注意:阅读/proc/PID/maps 或/proc/PID/smaps 本质上是不雅的(一致 输出只能在单个读取调用中实现(。 这通常在对这些文件进行部分读取时表现出来,而 正在修改内存映射。 尽管有比赛,我们确实提供以下内容 保证:

1(映射的地址永远不会倒退,这意味着没有两个区域会重叠。

2(如果在整个过程中给定的vaddr有东西 Smaps/Maps Walk的生活,会有一些输出。

所以在我看来,如果我在一次read()通话中得到它,我才能信任我得到的数据。 尽管缓冲区足够大,但它只返回一小块数据。 这反过来意味着实际上没有办法获得一致的/proc/<pid>/smaps快照,并且 cat/使用多个read()调用返回的数据可能是垃圾,具体取决于日月光比?

还是 2( 实际上意味着我对上面列出的先前 SO 答案太执着了?

您受到 fs/seq_file.c 中的内部内核缓冲区大小的限制,该大小用于生成许多/proc 文件。

缓冲区首先设置为页面大小,然后呈指数增长以容纳至少一条记录,然后塞满尽可能多的完整记录,但在能够容纳第一个条目后不再增长。当内部缓冲区无法容纳更多条目时,读取结束。

最新更新