lwip运行一段时间后ping时间延长

原文:http://blog.sina.com.cn/u/2766292570


很多网友在学习LWIP的时候,都遇到过:刚开始ping 设备的时候返回时间都在1ms以下,可是工作了一段时间后,ping返回的时间却越来越长,甚至达到了超时的程度,通过使用ip tool等抓包工具可以发现,其实是有数据返回的,只不过返回的间隔太久了,有的达到了3000ms以上,可是这已经超出了tcp的数据包间隔要求。一旦出现了这种情况以后,只有通过复位单片机才能恢复,别无他法。我在学习lwip的时候也遇到了这个问题,研究了好几次,都没能解决。

后来终于在网上看到了问题的原因:

使用了中断的方式接收网卡数据包,在每次中断中只读取网卡的一个数据包。如果一次中断发生,而网卡内事实上可能存在有多个数据包,这样如果只读一个,最终导致网卡内数据包积压,所以会出现ping延迟的现象。如果数据包长期积压,还可能出现因网卡缓冲区满而无法接收数据的情况,由于无接收中断产生,控制器也不会处理积压数据的现象,从而发生网卡假死的现象,系统无法接收数据。

      这里的正确解决方法是, 在一个中断中,需要读取并处理所有的已经接收的数据包

从网上可以看到,有些比较厉害的网友给出了解决方案,就是修改ethernetif.c文件中的ethernetif_input函数,修改后的函数:

 void ethernetif_input( void * pvParameters )
{
struct pbuf *p;
 
for( ;; )
{
if (xSemaphoreTake( s_xSemaphore, emacBLOCK_TIME_WAITING_FOR_INPUT)==pdTRUE)
{
TRY_GET_NEXT_FRAGMENT:
p = low_level_input( s_pxNetIf );
if (p != NULL)
{
if (ERR_OK != s_pxNetIf->input( p, s_pxNetIf))
{
pbuf_free(p);
p=NULL;
}
else
{
xSemaphoreTake( s_xSemaphore, 0);
goto TRY_GET_NEXT_FRAGMENT;
}
}  
 
}
}
}
可是我修改了之后,程序启动之后却直接崩溃了,这真心难过,好不容易找到了一个解决方案,却不能用,欲哭无泪啊。心中不免暗想:是不是网友没有测试过自己的代码呢?可是给出这种解决方案的不止一个网友,怎么可能是不能用的呢?于是我挂上了jlink,不调不知道,一条吓一跳,程序竟然进入了HardFault_Handler函数,崩溃了!很多网友都给出了以上的解决方案,可是却从没有人提过还会引入这个问题啊!没办法,问题既然出了,就只能想办法解决。
       经过一番周折之后,发现原来是frame.descriptor->Status = ETH_DMARxDesc_OWN;这条语句引起的。于是查看与它相关的函数调用,最终发现原来问题再这里:
static struct pbuf * low_level_input(struct netif *netif)
{
  struct pbuf *p, *q;
  u16_t len;
  int l =0;
  FrameTypeDef frame;
  u8 *buffer;
  
  p = NULL;
  frame = ETH_RxPkt_ChainMode();
当总线忙的时候, ETH_RxPkt_ChainMode();返回的frame没有给frame.descriptor指向一个地址,所以
后面在对这个地址变量赋值的时候就出错了
 
  frame.descriptor->Status = ETH_DMARxDesc_OWN; 
经过一番刻苦铭心的惨疼,终于找到了问题的症结,也算是没有白费这半天功夫。
解决方案:
                 在   frame = ETH_RxPkt_ChainMode();执行之后增加一个判断,便可解决这个问题
if(frame.length == ETH_ERROR) //总线错误,DMA现在忙
{
return p;
}
测试:
       在增加本文修改之前,只要在调试的时候设置断点,停下来后再运行,ping数据包,返回的时间基本都在几百毫秒,按照本文修改之后,又可以恢复到1ms以内了。困扰我几个月的问题,终于在今天得到了解决!
为了感谢网友的无私奉献,特记录今天的修改过程,给出解决方案,如有不对之处,还请指出。

你可能感兴趣的:(lwip,lwip)