STM32F407和LAN8720调试记录(4)

1.STM32F407和LAN8720调试记录(1)

2.STM32F407和LAN8720调试记录(2)

3.STM32F407和LAN8720调试记录(3)

之前是由陈工在进行LWIP的移植,之后陈工因为学业原因暂时离开公司,之后便由我接手后续的工作。所以和之前的博客地址不一样。我写的没有陈工那么好,但我会尽量按照比较清晰的逻辑把LWIP移植过程中的坑说明白。
经过前面三个实验,虽然网络已经能够正常收发了,可是有一些非常严重的问题

当有大量数据传输的时候,大概半个小时,网络通信就会挂。

这个问题有如下四个原因

原子例程中设置的存放数据的方式是HEAP

使用HEAP作为数据包储存方式会产生大量内存碎片,使得大数据包越来越难找到合适的内存来存放数据,只有将HEAP改为POOL才能解决这个问题。通过更改MEM_USE_POOLS和MEMP_USE_CUSTOM_POOLS这两个宏定义为1并且自己编写lwippool.h能够从HEAP切换为POOL。下面是我自己写的lwippool.h

#if MEM_USE_POOLS
LWIP_MALLOC_MEMPOOL_START
LWIP_MALLOC_MEMPOOL(10, 256)
LWIP_MALLOC_MEMPOOL(5, 512)
LWIP_MALLOC_MEMPOOL(3, 1024)
LWIP_MALLOC_MEMPOOL(1, 1536)
LWIP_MALLOC_MEMPOOL_END
#endif 

没有实现LWIP的锁

在LWIP里面存在不能被打断的操作,有操作系统的可以通过调用操作系统的函数来为这些操作加锁,没有操作系统的根据LWIP的建议是通过在进行这些操作时暂时关闭中断来达到不被打断的目的。LWIP中需要实现的加锁和解锁的代码是SYS_ARCH_DECL_PROTECT(lev),SYS_ARCH_PROTECT(lev)和SYS_ARCH_UNPROTECT(lev)。三个函数分别是创建锁这个变量,加锁和解锁三个行为。我们暂时还没有加上操作系统,所以我们通过暂时关闭中断来实现了加锁的功能,这段代码在lwip_sys.h里面

#define SYS_ARCH_DECL_PROTECT(lev) 
#define SYS_ARCH_PROTECT(lev)           ETH_DMAITConfig(ETH_DMA_IT_NIS|ETH_DMA_IT_R,DISABLE)
#define SYS_ARCH_UNPROTECT(lev)     ETH_DMAITConfig(ETH_DMA_IT_NIS|ETH_DMA_IT_R,ENABLE)

在实现上面两个方案过后,能长时间不再出现网络挂的问题,但是在一些极端情况下依旧存在问题,比如疯狂发送会造成大量处理量的包,所以对此我发现了下面两个bug

没考虑到在调用recvfrom之前来了两个数据包的情况

每当收到一个udp包的时候便会调用runAtSocketRecv,然后将这个udp包绑定到socketpool里面对应的IP地址和端口的sockinit里。然后我们需要调用recvfrom来将sockinit给取出来。可是这个sockinit一次只能对应一个LWIP里面的pbuf,也就是每次调用recvfrom之前如果从相同的端口来了两个包,那么需要将前面一个包给释放掉然后才能将新的包添加到socketpool,可是在runAtSocketRecv中并没有释放掉原来的包,而是直接将sockinit指向新的包,那么会造成的结果是,LWIP的pool会越用越少。所以我添加了判断

if(socketPool[s].recved == true)
{
    pbuf_free(socketPool[s].pp);
}

没考虑到在recvfrom的时候突然进入中断

pbuf_free(p);
socketPool[s].recved = false;
socketPool[s].pp = NULL;

这是在调用recvfrom的时候最后的代码,如果在pbuf_free之后突然进入由同一个端口造成的网络中断,socketPool[s].recved还是true此时根据我们上面的代码,我们会对同一个pbuf调用第二次pbuf_free,然后触发p->ref>0的警告,网上很多人在搜索这个警告要怎么解决,但是没有一个准确方案。这个警告的意思很明确,就是同一个pbuf被pbuf_free调用了两次,仔细找找一定是在什么地方想漏了。然后从中断中出来后,我们继续我们的主程序,将recved被设置为false。然后当我们下次调用recvfrom的时候,我们会发现recved为false认为没有收到包,然后一个pbuf将永远不会被放回lwip的pool里面,随着时间推移,触发这个罕见的情况的次数越来越多,最后导致lwip的pool里面一个空闲的pbuf都没有了。
所以我们需要在处理recvfrom的时候添加锁,这个锁依旧是停止中断。但是需要注意的是,由于LWIP里有解锁操作,像我这里用网络作为输出工具的话,即便加了锁,在发送数据的时候,会被SYS_ARCH_UNPROTECT解锁。从而再次进入中断,所以需要写一个变量判断是不是正在recvfrom的锁当中

通过上面四个处理方式,就不会再出现网络内存不够的情况

你可能感兴趣的:(STM32F407和LAN8720调试记录(4))