Lwip使用经验


  1. LWIP使用经验
    1. 一 LWIP内存管理
      1. 数据包管理
      2. 设置内存大小
      3. 宏编译开关
    2. 二 LWIP启动时序
    3. 三 LWIP运行逻辑
      1. 接收数据包
      2. SequentialAPI函数调用
    4. 四 TCPIP核心知识点
      1. 滑动窗口
      2. 三次握手
      3. 断开连接
      4. TCP状态转换
      5. 同时打开
      6. 同时关闭
    5. 五正确使用LWIP
    6. 六 LWIP常见问题
      1. 网卡驱动程序
      2. 内存泄露
      3. PC机无法与LWIP建立TCP连接


LWIP使用经验

 LWIP内存管理

LWIP的内存管理使用了2种方式:内存池memp和内存堆mem,如图1所示。

内存池的特点是预先开辟多组固定大小的内存块组织成链表,实现简单,分配和回收速度快,不会产生内存碎片,但是大小固定,并且需要预估算准确。

内存堆的本质是对一个事先定义好的内存块进行合理有效的组织和管理,主要用于任意大小的内存分配,实现较复杂,分配需要查找,回收需要合并,容易产生内存碎片,需要合理估算内存堆的总大小。

 

1内存池与内存堆

1.     数据包管理

数据包管理结构pbuf共有四种类型,它们的特点和使用场合如表1所示。

类别

分配方式

特点

使用场合

PBUF_RAM

内存堆,包括pbuf和数据区

长度不定,分配耗时

应用程序和协议栈

PBUF_POOL

内存池,包括pbuf和数据区

长度固定,分配快

中断服务程序

PBUF_ROM

内存池,仅包括pbuf

所指数据位于ROM

应用程序引用内存区

PBUF_REF

内存池,仅包括pbuf

所指数据位于RAM

应用程序引用内存区

1  pbuf类型与特点

每一种pbuf分配内存的方式都不一样,如图2所示。

2四种数据包管理结构

只有选择合适的pbuf类型才能发挥LWIP的最大性能,一个数据包可能是多种pbuf的组合,用链表连接起来,如图3所示。

3  pbuf链表

2.     设置内存大小

LWIP开辟一个专用的内存堆是应该的,这样一来LWIPmem_alloc()mem_free()都将基于该堆内存进行分配和回收,不影响其他系统内存的使用。如图1左所示,lwipopt.h文件中宏MEM_SIZE定义了堆区的大小,对于一个负荷较重的系统堆区需要分配较大。

4堆和PBUF_ROM内存区

LWIP使用PBUF_ROM类型的内存池来发送“只读”数据(处于ROM中或者其他进程中不可修改),宏MEMP_NUM_PBUF定义了该缓冲池的个数,如图2右所示。

ISR(中断服务程序中)经常需要快速分配一部分内存进行数据存储,这是通过PBUF_POOL类型的缓冲区实现的。为此需要定义两个宏:PBUF_POOL_SIZE定义缓冲池的个数,PBUF_POOL_BUFSIZE定义单个缓冲区的大小,如图5所示。

图5 PBUF_POOL内存区

宏编译开关

若定义MEM_LIBC_MALLOC=1,直接使用C库中的malloc、free来分配动态内存;否则使用LWIP自带的mem_malloc、mem_free等函数。

若定义MEMP_MEM_MALLOC=1,则memp.c中的全部内容不会被编译,用内存堆来实现内存池分配,使用这种方式得考虑是否能忍受内存堆分配带来的时间延迟。

若定义MEM_USE_POOLS=1,则mem.c中的全部内容不会被编译,用内存池来实现内存堆的分配,使用这种方式得考虑是否能忍受因为POOL内存固定大小而带来的内存浪费。此外用户需要定义宏MEM_USE_CUSTOM_POOLS=1,还需要额外实现一个头文件lwippools.h,并在其中开辟一些用于内存堆分配函数的内存池POOL,开辟空间的格式如下:

LWIP_MALLOC_MEMPOOL_START

LWIP_MALLOC_MEMPOOL(20, 256)

LWIP_MALLOC_MEMPOOL(10, 512)

LWIP_MALLOC_MEMPOOL_END

 LWIP启动时序

6展示了LWIP启动时序,大部分函数都是LWIP自带的,主要的移植代码是eth_init()实现初始化以太网接口,启动程序会创建2个线程:tcpip_thread负责LWIP的绝大部分工作(主要是协议栈的解析和系统运行),ethernetif_thread负责从网口接收数据包再交付给tcpip_thread线程进行处理。

图6 LWIP启动函数

 LWIP运行逻辑

接收数据包

图7接收数据包

当以太网口接收到一个数据包后,EMAC_IRQ中断服务程序通过信号量通知ethernetif线程,ethernetif线程调用low_level_input()接收该数据包并通过邮箱交付给tcpip_thread线程,tcpip_thread根据该数据包的类型进行相应处理。它是建立在消息传递的基础上的,如图8所示。

图8数据包消息的产生和处理

2  SequentialAPI函数调用

API设计的核心在于让用户进程负责尽可能多的工作,例如数据的计算、拷贝等;而协议栈进程只负责简单的通信工作,这是很合理的,因为系统可能存在多个应用程序,它们都使用协议栈进程提供的通信服务,保证内核进程的高效性和实时性是提高系统性能的重要保障。进程之间通信使用IPC技术,包括邮箱、信号量和共享内存,如图9所示。

图9协议栈API实现

以函数netconn_bind()为例看API是如何实现的,首先用户程序中调用函数netconn_bind()绑定一个连接,则这个函数实现时,是通过向内核进程发送一个TCPIP_MSG_API类型的消息,告诉内核进程执行do_bind函数:在消息发送后,函数阻塞在信号量上,等待内核处理该消息;内核在处理消息时,会根据消息内容调用do_bind,而do_bind会根据连接的类型调用内核函数udp_bind、tcp_bind或raw_bind;当do_bind执行完后,它会释放信号量,这使被阻塞的netconn_bind得以继续执行,整个过程如图10所示。

图10 API函数实现

 TCP/IP核心知识点

1.       滑动窗口

11滑动窗口

接收窗口相关的字段中,rcv_nxt是自己期望收到的下一个字节编号,rcv_wnd表示接收窗口的大小,rcv_ann_wnd表示将向对方通告的窗口大小值,这个值在报文发送时会被填在首部中的窗口大小字段,rcv_ann_right_edge记录了上一次窗口通告时窗口右边界取值。上面这四个字段都会随着数据的发送和接收动态地改变,如图12所示。

图12接收窗口

发送窗口中,lastack记录了被接收方确认的最高序列号,snd_nxt表示自己将要发送的下一个数据的起始编号,snd_wnd记录了当前的发送窗口大小,它常被设置为接收方通告的接收窗口值,snd_lbb记录了下一个被应用程序缓存的数据的起始编号,如图10所示。

上面这四个字段的值也是动态变化的,每当收到接收方的一个有效ACK后,lastack的值就做相应的增加,指向下一个待确认数据的编号,当发送一个报文后,snd_nxt的值就做相应的增加,指向下一个待发送数据,snd_nxtlastack之间的差值不能超过snd_wnd的大小。

由于实际数据发送时是按照报文段的形式组织的,因此可能存在这样的情况:即使发送窗口允许,但并不是窗口内的所有数据都能发送以填满窗口,如图13中编号为1113的数据,可能因为它们太小不能组织成一个有效的报文段,因此不会被发送。发送方会等到新的确认到来,从而使发送窗口向右滑动,使得更多的数据被包含在窗口中,这样再启动下一个报文段的发送。

13发送窗口

2.       三次握手

14连接建立过程

3.       断开连接

15连接断开过程

4.      TCP状态转换

16  TCP状态转换图

5.       同时打开

17双方同时打开

6.       同时关闭

 

18双方同时关闭

五正确使用LWIP

一般说来LWIP协议栈是比较稳定的,尤其像V1.3.2经历过业界广泛使用和工程应用,完全可以应用于嵌入式产品。那为什么还是有很多人反映LWIP不稳定呢?主要是以下几个方面的原因:

1.      网络硬件驱动 确保EMAC口接收与发送稳定可靠

2.      移植LWIP 基于OS的移植代码正确稳定

3.      配置LWIP 根据设备RAM尺寸进行合理配置

1)        值(PBUF_POOL_SIZE * PBUF_POOL_BUFSIZE)必须大于TCP_SND_BUFTCP_WND,否则容易引起错误;

2)        当内存有限时TCP发送不能太快(具体值依赖于分配内存的大小),否则引起tcp_enqueue()逻辑错误;

4.      调用LWIPAPI函数 正确使用API函数,特别防止内存泄露。

5.      资源不足 打开报警提醒,当资源不够时提醒设计者

 LWIP常见问题

1.       网卡驱动程序

首先,必须将协议栈完全初始化才能打开网络接收功能,接收中断必须将数据封装在PBUF中,然后交会给协议栈内核处理。其次,LWIP的全局变量(arp_table,netif_list,udp_pcbs等)确保赋初值0,否则容易一运行就崩溃。

2.       内存泄露

第一个原则(责任制):谁分配内存,谁就负责回收。

第二个原则(对称性):分配内存者与回收内存者一一对应构成闭环。

另外,需要特别注意一些系统函数的调用,它们也会带来内存泄露,如:

1

newconn = netconn_accept(conn);

do_something_for(newconn);

netconn_close(newconn);

netconn_delete(newconn);   /*一定要释放newconn否则将导致内存泄露 */

 

2

inbuf = netconn_recv(conn);

do_something_for(inbuf);

netbuf_delete(inbuf);   /*一定要释放inbuf否则将导致内存泄露 */

3.      PC机无法与LWIP建立TCP连接

问题:PC机能够与LWIP设备PING操作成功,但是无法建立TCP连接。

原因:通过代码跟踪,发现LWIP发出了SYN+ACK数据包,但是PC机无法接收该握手数据包,该数据包为60字节,小于以太网的最小长度(64字节),而LWIP设备的EMAC没有设置短小数据包填充功能,导致PC机无法接收该短数据包。

解决:使能EMAC的短小数据包填充功能。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/405314.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Keil5 编译生成bin二进制文件的设置方法

勾选:After Build/Rebuild Run #1 代码:fromelf --bin .\output\node.axf --output .\output\NoiseApp.bin 解释: .\ 指当前工程文件.uvprojx所在的目录.\output\node.axf 表示给定axf所在的位置.\output\NoiseApp.bin 表示bin文件输出的…

在word中插入代码段的方法[转]

废话不多说,下面说明实现步骤 步骤一 1.打开这个代码高亮工具网站:http://www.planetb.ca/syntax-highlight-word 2.在代码框中粘贴代码,选择语言,点击Show Highlighted 3.复制生成的代码段 步骤二 4.在 Microsoft Word…

开源好用的思维导图软件XMind

作为一款免费开源的思维导图制作编辑软件,灵活的运用思维导图会让你在学习和工作上帮助甚大。 接下来我们除了介绍思维导图之外,还给大家介绍一款免费的思维导图制作软件 XMind,它能支持 Windows、Mac、Linux 主流操作系统,而且还…

STM32到GD32移植攻略

1、 系统 1) 晶振起振区别 描述:启动时间,GD32 与STM32 启动时间都是2ms,实际上GD 的执行效率快,所以ST 的HSE_STARTUP_TIMEOUT ((uint16_t)0x0500)是2ms,但是这个宏定义值在GD 上时间就更加短了,所以要加大…

Emmet:HTML/CSS代码快速编写神器

本文来源:http://www.iteye.com/news/27580 ,还可参考:http://www.w3cplus.com/tools/emmet-cheat-sheet.htmlEmmet的前身是大名鼎鼎的Zen coding,如果你从事Web前端开发的话,对该插件一定不会陌生。它使用仿CSS选择…

HardFault_Handler问题查找方法

STM32出现HardFault_Handler故障的原因主要有两个方面: 1、内存溢出或者访问越界。这个需要自己写程序的时候规范代码,遇到了需要慢慢排查。 2、堆栈溢出。增加堆栈的大小。 出现问题时排查的方法: 发生异常之后可首先查看LR寄存器中的值&…

《深入理解Linux内核》笔记5:内存管理

本文介绍内核如何给自己分配物理内存并管理。对应《深入》第8章。 在《深入》第2章“内存寻址”(或者是我博客中的这篇文章,点这里)中,已经介绍了内核如何给自己分配1G的线性地址的。但是物理内存的分配及管理恐怕更复杂而且更有必…

Objective-C设计模式——单例Singleton(对象创建)

单例 和其它语言的单例产不多,可以说是最简单的一种设计模式了。但是有几个点需要注意下,单例就是一个类只有一个实例。 所以我们要想办法阻止该类产生别的实例,一般语言中都会将构造函数写为private。但是OC中的函数并没有限定符&#xff0c…

基于SSM在线协同过滤汽车推荐销售系统

SSM毕设分享 基于SSM在线协同过滤汽车推荐销售系统 1 项目简介 Hi,各位同学好,这里是郑师兄! 今天向大家分享一个毕业设计项目作品【】 师兄根据实现的难度和等级对项目进行评分(最低0分,满分5分) 难度系数:3分 工作…

Keil中的Code,RO,RW,ZI分别表示什么?

在使用keil开发STM32应用程序时,点击Build后在Build Output窗口中经常会有如下信息:以前一直好奇这几个参数和实际使用的STM32芯片中Flash和SRAM的对应关系,于是上网搜了一圈,做如下总结:这些参数的单位是Byte图中几个…

JTAG、SWD接口定义

版权声明:本文为博主原创文章,转载请注明。 博客已转到 http://blog.csdn.net/upc_xbt https://blog.csdn.net/u014124220/article/details/50829713Jlink仿真器接口仿真器端口连接目标板备注1. VCCMCU电源VCCVCC2. VCCMCU电源VCCVCC3. TRSTTRSTTest ReS…

Drainage Ditches - poj 1273(网络流模板)

题意:1是源点,m是汇点,求出来最大流量,没什么好说的就是练习最大流的模板题 ************************************************************** 先用Edmonds-Karp的算法做一下试试吧重边贡献了 1W,要加上所有的重边才算…

linux RTC 驱动模型分析

linux RTC 驱动模型分析RTC(real time clock)实时时钟,主要作用是给Linux系统提供时间。RTC因为是电池供电的,所以掉电后时间不丢失。Linux内核把RTC用作“离线”的时间与日期维护器。当Linux内核启动时,它从RTC中读取时间与日期,…

Install Docker Mac OS X

检查 Mac OS version 要求必须是 OS X 10.6 Snow Leopard or newer to run Boot2Docker安装 Boot2Docker 列表内容下载地址:https://github.com/boot2docker/osx-installer/releases/download/v1.7.0/Boot2Docker-1.7.0.pkg 下载后点击安装,就是按照提示…

linq to sql报错,

以上是由于我把关联表中的string类型写成int类型所导致的,记一下,备用。转载于:https://www.cnblogs.com/server126/archive/2011/05/25/2057416.html

[VC6] RadioBox使用入门

基于对话框的应用程序,界面如下: 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 具体请参考代码: 关键代码: // del2Dlg.cpp : implementation file //#include "stdaf…

树莓派使用STEP1:装系统

1、windows安装SD卡格式化软件:SDFormatter.exe 2、windows安装系统烧录软件Win32DiskImager.exe 3、下载镜像:https://www.raspberrypi.org/downloads/raspbian/ 并解压缩 4、用Win32DiskImager将下载的镜像文件烧录进SD卡。 5、插上SD到树莓派&…

树莓派使用STEP2:设置网络

安装好系统并开机,插入鼠标键盘和显示器,登陆系统,打开终端。按以下操作。 1、sudo raspi-config 进入配置,打开SSH功能。 Interfacing Options -> SSH 2、扩展系统内存。 Advanced options -> Expand Filesystem 3、设置…

linux3.0-内核自带led驱动移植

********************************************************************************************************************************************************************************** cpu : s3c2440(arm9) linux内核:linux-3.0 开发板 &am…

树莓派使用STEP3:更换镜像源

更换国内镜像源,这里使用中科大的源,软件下载和更新速度更快。 1、sudo nano /etc/apt/sources.list 2、sudo nano /etc/apt/sources.list.d/raspi.list 3、sudo apt-get update && apt-get upgrade