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

Sou*_*yuu 6 linux procfs

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

根据这个 SO answer/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

...
Run Code Online (Sandbox Code Playgroud)

我现在的问题是,尽管我使用了 100kB 的缓冲区,但只返回了 3722 个字节。查看cat使用 strace 解析文件时的作用,我看到它使用多次调用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 +++
Run Code Online (Sandbox Code Playgroud)

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

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

要查看某个时刻的精确快照,您可以查看 /proc/<pid>/smaps 文件并扫描页表。

然后在后面的文字中说:

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

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

2) 如果在 smaps/maps walk 的整个生命周期中在给定的 vaddr 上有东西,就会有一些输出。

所以在我看来,我只能相信我在一次read()调用中获得的数据。尽管缓冲区足够大,但它只返回一小块数据。这反过来意味着实际上无法获得一致的快照,/proc/<pid>/smaps并且read()根据日月光比,cat/使用多次调用返回的数据可能是垃圾?

或者 2) 是否真的意味着我对上面列出的上一个 SO 答案太着迷了?

OhJ*_*eez 3

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

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