LWIP memory leak: solved

LWIP内存泄露问题

最近在项目中遇到了使用LWIP 1.4.1协议栈内存泄露的问题。表现为使用socket进行通信过程中,有时fd 资源已释放的情况下,网络堆内存依然没被释放。经过长时间的积累,导致无法申请网络堆内存。
这种情况在网络物理连接断开的情况下特别容易出现,比如插拔网线。

问题分析

  1. TCP三握手四挥手 。是否因为close fd 时TimeWait时间较长,导致网络堆内存释放慢。
    在代码中没有搜索到与TimeWait相关的参数,因此只能想其他办法。
  2. LWIP 释放内存出Bug
    通过内存检测工具定位内存泄露的位置。排查原因。

解决问题过程

  1. 通过对问题分析中提到的第二点进行了调试,在网上找到一个好用的内存泄露检测工具memleak (非常适合嵌入式C平台), 进行内存调试。

    memleak 下载地址
    sourceforge下载源码地址:http://sourceforge.net/projects/memleak/

    通过定位,发现是在tcp 发送相关的lwip函数调用中没有释放内存。还发现通过get_socket函数找到fd并调用tcp_abort函数主动释放堆内存能解决问题。

    但这个方法非常不安全,因为破坏了LWIP内部机制。

  2. 在无意中注意到lwipopts.h中配置文件关于KeepAlive的心跳检测机制的参数。这个正确设置好了就能解决LWIP内存泄露的问题。

#define TCP_KEEPIDLE_DEFAULT			10000UL
#define TCP_KEEPINTVL_DEFAULT			1000UL
#define TCP_KEEPCNT_DEFAULT             10U

搜索代码发现这个参数默认值是上述宏配置,可以通过setsockopt函数配置针对心跳包时间进行修改。
之前工程中有设置这个选项,但不起作用。现在深入看了LWIP源码,才发现里面涉及到单位转换问题。
如果单位搞错了,就不起作用了。
在sockets.h文件中,看到了与时间单位相关的参数。

/*
* Options for level IPPROTO_TCP
*/
#define TCP_NODELAY    0x01    /* don't delay send to coalesce packets */
#define TCP_KEEPALIVE  0x02    /* send KEEPALIVE probes when idle for pcb->keep_idle milliseconds */
#define TCP_KEEPIDLE   0x03    /* set pcb->keep_idle  - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt */
#define TCP_KEEPINTVL  0x04    /* set pcb->keep_intvl - Use seconds for get/setsockopt */
#define TCP_KEEPCNT    0x05    /* set pcb->keep_cnt   - Use number of probes sent for get/setsockopt */
#endif /* LWIP_TCP */

所以只有正确的设置setsockopt, 心跳包才起作用。下面是我封装的一个设置心跳包参数的函数。

int TCPSetKeepAlive( int sockfd, int keepAlive, int keepIdle, int keepInterval, int keepCount ){

   int tvKeepAlive = keepAlive;
   int tvKeepIdle = keepIdle/1000;
   int tvKeepInterval = keepInterval/1000;
   int tvKeepCount = keepCount;

   if( setsockopt( sockfd, SOL_SOCKET, SO_KEEPALIVE, ( void * )&tvKeepAlive, sizeof( int ) ) < 0 )
       DFULLOGE("Set setsockopt keep alive failed");
   if( setsockopt( sockfd, IPPROTO_TCP, TCP_KEEPIDLE, ( void * )&tvKeepIdle, sizeof( int ) ) < 0 )
       DFULLOGE("Set setsockopt keep idle failed");
   if( setsockopt( sockfd, IPPROTO_TCP, TCP_KEEPINTVL, ( void * )&tvKeepInterval, sizeof( int ) ) < 0 )
       DFULLOGE("Set setsockopt keep interval failed");
   if( setsockopt( sockfd, IPPROTO_TCP, TCP_KEEPCNT, ( void * )&tvKeepCount, sizeof( int ) ) < 0 )
       DFULLOGE("Set setsockopt keep count failed");

   return 0;
}

调用方法:

//使能心跳包
TCPSetKeepAlive( tvCliSock, 1, 20*1000, 5000, 2 );

关于心跳包的介绍可以参考文章:
https://blog.csdn.net/weixin_37672169/article/details/80283935

总结

绕了一个大弯,后面才意识到原来是心跳检测机制用法不对,导致网络堆内存释放时间长。

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页