Внутренне все интерфейсы NSS (из которых getaddrinfo
- один) выглядят как gethostbyname_r
:
int gethostbyname_r(const char *name,
struct hostent *ret, char *buf, size_t buflen,
struct hostent **result, int *h_errnop);
вызывающий абонент предоставляет буфер для данных результата через buf
, buflen
байтов. Если окажется, что этого буфера недостаточно по размеру, функция завершается с ошибкой ERANGE
. Ожидается, что вызывающая сторона увеличит буфер (перераспределит его каким-то образом) и вызовет функцию с другими параметрами, такими же. Это повторяется до тех пор, пока буфер не станет достаточно большим и функция завершится успешно (или функция завершится сбоем по какой-либо другой причине). Это длинная история о том, как мы получили этот странный интерфейс, но это те интерфейсы, которые мы имеем сегодня. getaddrinfo
выглядит по-другому, но реализации внутренней поддержки очень похожи на функцию publi c gethostbyname_r
.
Поскольку идиома повторных попыток с большим буфером так распространена во всем коде NSS , struct scratch_buffer
было введено. (Раньше было довольно смешанное c сочетание фиксированных размеров буфера, alloca
, alloca
с malloc
отступом и т. Д.) struct scratch_buffer
объединяет используемый в стеке буфер фиксированного размера, который используется для первого вызова NSS. Если это не удается с ERANGE
, вызывается scratch_buffer_grow
, который переключается на буфер кучи и при последующих вызовах выделяет больший буфер кучи. scratch_buffer_free
освобождает буфер кучи, если он есть.
В вашем примере утечки, которые tcmalloc
сообщает, не связаны с чистыми буферами. (У нас, конечно, были такие ошибки в getaddrinfo
, особенно на непонятных путях ошибок, но текущий код должен быть в основном нормальным.) Порядок ссылок также не является проблемой, потому что, очевидно, tcmalloc
активен, иначе вы бы не получили никаких отчеты об утечках.
Причина, по которой вы видите утечки с tcmalloc
(но не с другими инструментами, такими как valgrind), заключается в том, что tcmalloc
не вызывает функцию magi c __libc_freeres
, которая была специально добавлено для проверки кучи. Обычно, когда процесс завершается, glib c не освобождает все внутренние выделения, потому что ядро все равно освободит эту память. Большинство подсистем регистрируют там распределения каким-либо образом с __libc_freeres
. В примере getaddrinfo
я вижу следующие еще выделенные ресурсы:
- Результаты анализа
/etc/resolv.conf
(конфигурация DNS системы). - Результаты анализа
/etc/nsswitch.conf
( Конфигурация NSS). - Различные структуры данных загрузчика динамического c, возникающие в результате внутренних вызовов
dlopen
(для загрузки модулей служб NSS. - Кэш для записи поддержки системы IPv4 / IPv6 в
getaddrinfo
.
Вы можете легко увидеть эти распределения, если запустите свой пример под valgrind, используя такую команду:
valgrind --leak-check=full --show-reachable=yes --run-libc-freeres=no
Ключевая часть - --run-libc-freeres=no
, который инструктирует valgrind , а не , вызывать __libc_freeres
, что он делает по умолчанию. Если вы пропустите этот параметр, valgrind не будет сообщать о каких-либо утечках памяти.