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文件输出的…

数据库三大范式详解

引言数据库的设计范式是数据库设计所需要满足的规范,满足这些规范的数据库是简洁的、结构明晰的,同时,不会发生插入(insert)、删除(delete)和更新(update)操作异常。反之…

安防监控系统CIF、D1等格式的解释

From: http://blog.sina.com.cn/s/blog_5df7d82d0100ppox.html 在目前国内监控行业中,涉及视频采集卡、压缩卡、硬盘录像机(DVR)等产品时,经常会遇到诸如SIF、CIF、4CIF、D1等等分辨率标准名词。而在实际上,由于安防行…

[react] 有使用过Consumer吗

[react] 有使用过Consumer吗 Consumer主要用来在使用context的过程中,来获取上层provider的值,通过定义组件contextType的值来指定要获取的是哪个context,找到当前context对应的最近的一个provider,取到value属性的值&#xff0c…

查看线程CPU利用率

查看线程CPU利用率 方法1:利用ps命令查看对应的线程 1. ps -ef | grep 进程名称 2. ps -mp 进程ID -o THREAD,pid,tid,cmd,time,%cpu,%mem 3.1. gdb 3.2. attach 进程ID 3.3. info threads 3.4. thread 线程ID 3.5. bt等操作 3.6. detach 方法2:利用top命令…

linux USB驱动

http://blog.csdn.net/zqixiao_09/article/details/50984074

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

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

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

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

vc++6.0 模拟鼠标点击代码 木马程序的编写 VC 模拟键盘输入

From: http://fengqing888.blog.163.com/blog/static/33011416201112124481/ MFC 工程 把以下代码放到你想要响应的函数里面就行 CPoint pt; GetCursorPos(&pt);//获取鼠标在屏幕的当前位置 SetCursorPos(100,200);//移动到某点坐标 mouse_event(MOUSEEVENTF_RIGHTDO…

[react] 你有用过React的插槽(Portals)吗?怎么用?

[react] 你有用过React的插槽(Portals)吗?怎么用? 1、首先简单的介绍下react中的插槽(Portals),通过ReactDOM.createPortal(child, container)创建,是ReactDOM提供的接口,可以实现将子节点渲染…

STM32到GD32移植攻略

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

安装SQL2005提示“SQL Server 2005 COM+ 目录要求”警告 解决方法

From: http://blog.csdn.net/yaday/article/details/4099223 SQL Server 2005 COM 目录要求警告 ①。启动Distributed Transaction Coordinator 服务 打开服务,在服务中找到Distributed Transaction Coordinator服务,选择“属性”; …

Qt 设置当前窗口出现在左右窗口的最前面

一、需求 点击按钮,出现且只出现一个子界面,即单一实例。 二、实现 void MainWindow::ShowHelpinfoUI(void) {static showhelpinfo_UI *myui new showhelpinfo_UI;myui->show();myui->activateWindow();myui->setWindowState((myui->win…

过程重要,还是结果重要?

当我们在谈到自己的失败时,我们总是在说我们在失败中学到了什么,而不去计较最终的结果是什么。但当我们谈到某位成名的英雄时,总是在谈论他的累累硕果,而很少谈及他在走向成功道路上的付出。究竟是过程重要呢,还是结果…

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

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

[react] React的严格模式有什么用处?

[react] React的严格模式有什么用处? react的strictMode 是一个突出显示应用程序中潜在问题的工具,与Fragment一样,strictMode 不会渲染任何的可见UI,它为其后代元素触发额外的检查和警告。 注意:严格模式仅在开发模…

HardFault_Handler问题查找方法

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

VMware Workstation 运行出现“由于应用程序配置不正确,应用程序未能启动。重新安装应用程序可能会纠正这个问题”解决方案

From: http://blog.csdn.net/lasig/article/details/5694895 今天安装完VMware Workstation 6.5.2之后在运行时,遇到“由于应用程序配置不正确,应用程序未能启动。重新安装应用程序可能会纠正这个问题”的提示。当我们遇到这个问题的时候,解决方法就是…

[react] 可以使用TypeScript写React应用吗?怎么操作?

[react] 可以使用TypeScript写React应用吗?怎么操作? 使用ts启动新的 create react app项目yarn create react-app my-app --typescript 将ts添加到已经创建好的create react app项目中yarn add typescript types/node types/react types/react-dom typ…

QT 获取系统时间

一、需求 获取系统时间&#xff0c;生成日志唯一名称。 二、代码 #include <QDateTime> QDateTime current_date_time QDateTime::currentDateTime(); QString current_date current_date_time.toString("yyyy_MM_dd_hh_mm_ss");