【发布时间】:2020-01-14 16:26:53
【问题描述】:
我正在尝试从我的 C++ 二进制文件中一个进程的/proc/<pid>/smaps 解析 PSS 值。
根据this 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
...
我现在的问题是,尽管我使用了 100kB 的缓冲区,但只返回了 3722 个字节。查看 cat 在使用 strace 解析文件时所做的事情,我发现它正在使用对 read() 的多次调用(每次读取也会获得大约 3k 字节),直到 read() 返回 0 - 如 documentation 中所述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 here 的信息,似乎支持之前的 SO 答案:
查看精确的 瞬间快照,可以看到 /proc/
/smaps 文件和扫描页表。
然后在正文中说:
注意:读取 /proc/PID/maps 或 /proc/PID/smaps 本质上是活泼的(一致 输出只能在单个读取调用中实现)。 这通常在对这些文件进行部分读取时表现出来,而 正在修改内存映射。 尽管有比赛,我们确实提供以下服务 保证:
1) 映射的地址永远不会后退,这意味着两个区域永远不会重叠。
2) 如果在整个 smaps/maps walk 的生命周期,会有一些输出。
所以在我看来,我只能相信我在单个read() 调用中获得的数据。
尽管缓冲区足够大,但它只返回一小部分数据。
这反过来意味着实际上没有办法获得/proc/<pid>/smaps 的一致快照,并且cat/使用多个read() 调用返回的数据可能是垃圾,具体取决于日月光比?
或者 2) 实际上是否意味着我对上面列出的上一个 SO 答案太着迷了?
【问题讨论】: