LwIP从入门到放弃之(三)---数据包管理

我一开始看LwIP协议栈的时候,发现pbuf这个数据类型出现的非常频繁,我就感觉这个数据类型肯定不一般。现在终于知道,pbuf是描述和管理数据包的结构,在整个协议栈中是非常重要的部分。其实,协议栈的本质就是各层对数据包的处理。在数据链路层,判断数据包的类型、提取数据包的数据字段、记录物理地址信息;IP层根据数据包中的IP地址进行数据存储、转发;TCP使用数据包中的信息更新TCP状态机,并向应用层提交程序等等。上述所有操作过程都与数据包操作密切相关。

1. 数据包结构

在LwIP中,文件pbuf.h和pbuf.c实现了协议栈数据包管理的所有数据结构和函数。pbuf的定义如下:

struct pbuf {
	struct pbuf *next;  //构成pbuf链表时指向下一个pbuf结构
	void   *payload;    //数据指针,指向数据区域
	u16_t  tot_len;     //当前pbuf和后续所有pbuf中包含的数据总长度
	u6_t   len;         //当前pbuf数据的长度
	u8_t   type;        //当前pbuf的类型
	u8_t   flags;       //状态位,未用到
	u16_t  ref;         //该pbuf被引用的次数
	}

下面介绍一下结构体每一个成员的含义以及作用:
next字段指针指向下一个pbuf结构,因为实际发送或接收的数据包可能很大,而每个pbuf能够管理的数据可能会有限,所以,存在需要多个pbuf结构才能完全描述一个数据包的情况。此时,所有描述同一个数据包的pbuf需要连接在一个链表上,称之为pbuf链表,这一点用next实现。
payload是数据指针,指向该pbuf管理的数据起始地址。这里,数据起始地址可以是紧跟在pbuf结构之后的RAM空间中,也可能处在ROM中的某个地址上,而决定这点的是当前pbuf的类型,即type字段的值,在下面将继续讨论。
len字段表示当前pbuf中的有效数据长度,而tot len表示当前pbuf和其后所有pbuf的有效数据的总长度。显然,tot_len字段是len字段与pbuf链表中下一个pbuf的tot_len字段之和;pbuf链表中第一个pbuf的tot_len字段表示整个数据包的长度,而最后一个pbuf的tot_len字段必同len字段相等(只有在很特殊的情况下,才可能存在一条pbuf链表上保存多个数据包的情况)。
type字段表示pbuf的类型,具体来说pbuf有四种类型,放在稍后来说。
flags字段在源代码中并未被使用到,在初始化一个pbuf的时候,该字段的值通常被设为0。
最后ref字段表示该pbuf被引用的次数,这里又是一个纠结的地方啊!引用表示有其他指针指向当前pbuf,这里的指针可以是其他pbuf的next指针,也可以是其他任何形式的指针。初始化一个pbuf的时候,ref字段值被设置为1(因为该pbuf的地址一定会被返回给一个指针变量),当有其他指针需要指向该pbuf时,必须调用相关函数将pbuf的ref字段值增加。

2. pbuf的类型

Pbuf有4类,PBUF_RAM、PBUF_ROM、PBUF_REF和PBUF_POOL。
PBUF_RAM时通过内存堆分配的,也是协议栈中使用最多的,这里着重讲解,其余几种pbuf类型,读者感兴趣,可以自行解读。
系统调用内存分配函数mem_malloc()进行内存分配的。分配的空间大小包括:pbuf结构大小SIZEOF_STRUCT_PBUF,需要数据存储空间大小length,还有一个offset。
在这里插入图片描述
细心的读者会发现,payload并没有指向数据区的其实位置,而是隔了一段区域,这一块区域就是上面的offset。Offset的作用通常用来保存各类数据包的首部字段,例如TCP报文首部,IP报文首部,以太网帧首部等。预留这些空间,使得数据包在各层协议中得到灵活处理。

3. 数据包申请函数

数据包申请函数pbuf_alloc在很多地方都被用到:
struct pbuf * pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
它有两个重要参数,一个是想申请的pbuf的类型,这个前面刚刚说过。另一个重要的参数是该数据包在哪一层被申请的,分配函数会根据层次的不同,在pbuf数据区域前为相应的协议留出首部空间,即offset。层次的定义通过一个枚举类型来实现的,如下代码所示:

typedef enum {
  PBUF_TRANSPORT,       //传输层
  PBUF_IP,              //网络层
  PBUF_LINK,            //链路层
  PBUF_RAW              //原始层,不预留任何空间
} pbuf_layer;

值得一提的是,分配函数分配空间的时候,根据不同的层次预留空间,当数据包逐层递交的时候,各层协议就能直接操作这些预留空间中的数据,以实现数据包首部的填写,这样也就避免的数据拷贝的过程。

4. 数据包释放函数pbuf_free()

在展开讨论之前,我们先确认数据包被释放的前提。前面说到pbuf的ref字段表示pbuf被引用的次数,当pbuf被创建时,初始值为1,以后每被引用一次,ref加1;删除pbuf时,函数会将ref减1,只有当pbuf的ref字段为0 的时候,该pbuf才能被删除。
数据包pbuf的删除工作可以描述为下面一个简单过程,某个pbuf链表的首节点删除成功后,该pbuf链的第二个节点就自然地成为了首节点,此时,该节点的ref值可能变为0(该节点没有在其他地方被引用了,而是只被已删除的首节点引用过),这种情况下,该节点也会被删除,因为LwIP认为它和第一个节点一起存储同一数据包。当第二个节点被删除后,数据包删除函数又会去看看第三个节点是否满足删除条件…就这样一直删下去。当然,如果首节点删除后,第二个节点的ref值大于0,表示该节点还在其他地方被引用,不能再被删除,删除工作至此结束。这段话写的很口水,不如举个例子来看看这个删除过程。假如现在我们的pbuf链表由A,B,C三个pbuf结构连接起来,结构为A—>B—>C,利用pbuf_free(A)函数来删除pbuf结构,下面用ABC的几组不同ref值来看看删除结果:
(1)1->2->3函数执行后变为…1->3,节点BC仍在;
(2)3->3->3函数执行后变为2->3->3,节点ABC仍在;
(3)1->1->2函数执行后变为…1,节点C仍在;
(4)2->1->1函数执行后变为1->1->1,节点ABC仍在;
(5)1->1->1函数执行后变为…,节点全部被删除。

注:LwIP协议栈源代码我已上传,需要的小伙伴欢迎下载:
https://download.csdn.net/download/rgxiwei/15724471

  • 0
    点赞
  • 2
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

房东的猫爱喵喵喵

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值