我正在尝试从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 文件。
缓冲区首先设置为页面大小,然后呈指数增长以容纳至少一条记录,然后塞满尽可能多的完整记录,但在能够容纳第一个条目后不再增长。当内部缓冲区无法容纳更多条目时,读取结束。