Каков правильный способ получить непротиворечивый снимок /proc/pid/smaps?

Я пытаюсь проанализировать значение PSS из /proc/<pid>/smaps процесса в моем двоичном файле C++.

Согласно этому ответу 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

...

Моя проблема сейчас в том, что, несмотря на то, что я использую буфер размером 100 КБ, возвращается только 3722 байта. Глядя на то, что делает cat при анализе файла с помощью strace, я вижу, что он использует несколько вызовов read() (также получая около 3 КБ при каждом чтении), пока 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/‹pid›/smaps и отсканировать таблицу страниц.

Далее в тексте говорится:

Примечание: чтение /proc/PID/maps или /proc/PID/smaps по своей сути является колоритным (непротиворечивый вывод может быть достигнут только в одном вызове чтения). Обычно это проявляется при частичном чтении этих файлов во время изменения карты памяти. Несмотря на гонки, мы предоставляем следующие гарантии:

1) Сопоставленные адреса никогда не идут назад, что означает, что никакие два региона никогда не перекрываются.

2) Если есть что-то в данном vaddr в течение всей жизни smaps/maps walk, для этого будет какой-то вывод.

Поэтому мне кажется, что я могу доверять полученным данным только в том случае, если я получу их за один вызов read(). Который возвращает только небольшой фрагмент данных, несмотря на то, что буфер достаточно велик. Что, в свою очередь, означает, что на самом деле нет способа получить непротиворечивый снимок /proc/<pid>/smaps, а данные, возвращаемые cat/использованием нескольких вызовов read(), могут быть мусором в зависимости от соотношения освещенности солнца и луны?

Или 2) на самом деле означает, что я слишком зациклен на предыдущем ответе SO, указанном выше?


person Soukyuu    schedule 14.01.2020    source источник


Ответы (1)


Вы ограничены размером внутреннего буфера ядра в файле fs/seq_file.c, который используется для создания многих файлов /proc.

Буфер сначала устанавливается равным размеру страницы, а затем экспоненциально увеличивается до размера хотя бы одной записи, а затем заполняется как многими целыми записями, как будет fit, но больше не увеличивается после того, как может соответствовать первой записи. Когда внутренний буфер не может вместить больше записей, чтение завершается.

person OhJeez    schedule 04.02.2021