You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Valgrind在测试程序中未检测到getinterfaceip函数的内存泄漏

Valgrind内存泄漏排查疑问

问题场景

主程序运行时Valgrind报告了内存泄漏,但将getinterfaceip的逻辑复制到简单C测试程序中用Valgrind检测时,却未发现任何泄漏。想确认该泄漏是否真的来自getinterfaceip函数。

Valgrind泄漏报告

==51== 200 bytes in 1 blocks are definitely lost in loss record 348 of 417
==51==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==51==    by 0x508CF54: __libc_alloc_buffer_allocate (alloc_buffer_allocate.c:26)
==51==    by 0x5130628: alloc_buffer_allocate (alloc_buffer.h:143)
==51==    by 0x5130628: __resolv_conf_allocate (resolv_conf.c:411)
==51==    by 0x512DE21: __resolv_conf_load (res_init.c:592)
==51==    by 0x5130232: __resolv_conf_get_current (resolv_conf.c:163)
==51==    by 0x512E3D4: __res_vinit (res_init.c:614)
==51==    by 0x512F5CF: maybe_init (resolv_context.c:122)
==51==    by 0x512F5CF: context_get (resolv_context.c:184)
==51==    by 0x512F5CF: context_get (resolv_context.c:176)
==51==    by 0x512F5CF: __resolv_context_get (resolv_context.c:195)
==51==    by 0x511E63F: gethostbyname (getXXbyYY.c:105)
==51==    by 0x153745: getinterfaceip (sctpThread.cpp:72)
==51==    by 0x153745: startPrometheus(sctp_params&) (sctpThread.cpp:539)
==51==    by 0x145051: main (sctpThread.cpp:608)

主程序相关源码

char* getinterfaceip()
{
   char hostname[256];
   char *IP;
   struct hostent *host_entry;
   int retVal;
   retVal = gethostname(hostname, sizeof(hostname));
   if ( retVal == -1 )
       return NULL;
   host_entry = gethostbyname(hostname);
   if ( host_entry == NULL )
       return NULL;
   IP = inet_ntoa(*((struct in_addr*) host_entry->h_addr_list[0]));
   return IP;
}

void startPrometheus(sctp_params_t &sctpParams) {
    auto podName = std::getenv("POD_NAME");
    string metric = "E2TBeta";
    if (strstr(podName, "alpha") != NULL) {
        metric = "E2TAlpha";
    }
    //Get eth0 interface IP
    char* host = getinterfaceip();
    string hostip = host;

    sctpParams.prometheusFamily = &BuildCounter()
            .Name(metric.c_str())
            .Help("E2T instance metrics")
            .Labels({{"POD_NAME", sctpParams.podName}})
            .Register(*sctpParams.prometheusRegistry);

    string prometheusPath;
    if (hostip.empty())
        prometheusPath = sctpParams.prometheusPort + "," + "[::]:" + sctpParams.prometheusPort;
}

测试程序源码

#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/inotify.h>
#include <errno.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <netdb.h>

main()
{
   char hostname[256];
   char *IP;
   struct hostent *host_entry;
   int retVal;
   retVal = gethostname(hostname, sizeof(hostname));
   if ( retVal == -1 )
       return NULL;
   host_entry = gethostbyname(hostname);
   if ( host_entry == NULL )
       return NULL;
   IP = inet_ntoa(*((struct in_addr*) host_entry->h_addr_list[0]));
   printf("%s", IP);
   sleep(300);
}

结论与分析

  1. 泄漏确实和getinterfaceip的调用有关,但并非函数本身的代码问题,而是触发了libc内部解析器的内存分配:
    • Valgrind调用栈清晰显示,泄漏发生在gethostbyname内部的__resolv_conf_allocate等glibc内部函数中,getinterfaceip是触发这个调用链的入口。
  2. 测试程序和主程序的差异导致Valgrind报告不同:
    • 测试程序是短期运行进程,退出时操作系统会回收所有进程内存,Valgrind默认会忽略进程退出时的内存泄漏(因为这类泄漏不会长期占用系统资源)。
    • 主程序是长期运行的服务进程,不会快速退出,glibc为DNS解析分配的内部缓存内存(比如resolv.conf相关结构)没有被主动释放,因此Valgrind会报告为明确泄漏。
  3. 额外说明:
    • inet_ntoa返回的是静态缓冲区指针,不需要手动释放,这部分无泄漏;gethostbyname返回的host_entry是libc内部缓存,也不需要手动free。
  4. 解决建议:
    • 若使用glibc,可尝试调用res_nclose重置解析器上下文,释放内部缓存,但注意这可能影响其他依赖DNS解析的代码。
    • 替换为现代线程安全APIgetaddrinfo,它的内存管理更透明,可通过freeaddrinfo手动释放资源,避免这类内部缓存泄漏。

内容的提问来源于stack exchange,提问作者ravi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 17:43:12