欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > 4. LwIP_网络数据包管理

4. LwIP_网络数据包管理

2025/1/20 18:55:34 来源:https://blog.csdn.net/Fresh_man111/article/details/145239800  浏览:    关键词:4. LwIP_网络数据包管理

概述

协议栈的本质:

TCP/IP协议栈的实现,本质上就是对数据包的管理。在LwIP中,定义了一个pbuf结构体对数据包进行管理。

pbuf管理数据包的步骤:

1、用户产生要传输的数据

2、用户在内存堆/内存池中申请一个pbuf结构体

3、将数据拷贝到申请的内存中,加上应用层的首部

4、依次加上各层的首部,直至发送出去

pbuf相关特征:

pbuf管理的数据能够在各层去访问,即:每个层访问的数据内存空间是一个空间,而不是每层之间相互独立。

pbuf结构体:

struct pbuf {struct pbuf *next;void *payload;u16_t tot_len;u16_t len;u8_t type_internal;u8_t flags;LWIP_PBUF_REF_T ref;u8_t if_idx;LWIP_PBUF_CUSTOM_DATA
};

next:当一个数据段需要分段来管理时会有多个pbuf,这些pbuf以链表形式进行连接

payload:指向数据区的指针

tot_len:pbuf的总长度,tot_len = len+next->len+....

len:当前pbuf中数据的总长度 

type_internal:pbuf的类型,该值定义在pbuf_type枚举类型中

pbuf的类型分配方式pbuf内容
PBUF_RAM内存堆pbuf控制头+数据区域 (一般情况下使用)
PBUF_POOL内存池pbuf控制头+数据区域 (常用于中断)
PBUF_ROM内存池只有pbuf控制头,数据区域在ROM中
PBUF_REF内存池只有pbuf控制头,数据区域在RAM中

ref:pbuf被引用的次数

pbuf_layer枚举类型:

pbuf_layer规定了pbuf的层次,不同的层次对应了不同的预留的报头空间。 

typedef enum {PBUF_TRANSPORT = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN,PBUF_IP = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN,PBUF_LINK = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN,PBUF_RAW_TX = PBUF_LINK_ENCAPSULATION_HLEN,PBUF_RAW = 0
} pbuf_layer;

PBUF_TRANSPORT:传输层报头的空闲空间,一般发送数据选择该层次

PBUF_IP:网络层IP头的空闲空间,用作ICMP协议发送

PBUF_LINK:链路层报头(IP分段、ARP数据包)

PBUF_RAW_TX:以太网前附加封装头的空闲空间

PBUF_RAW:原始层,不预留任何空间,即:没有报头,只有数据

相关函数

1、pbuf_alloc

函数功能:

根据类型(pbuf_type),申请pbuf内存。

函数结构:

通过传入的类型参数,进行相应的内存申请,整个函数就一个switch-case

  • PBUF_REF、PBUF_ROM:

它们就是在MEMP_PBUF内存池申请一个pbuf结构体空间,payload指向NULL

  • PBUF_POOL

它是在MEMP_PBUF_POOL内存池申请一个pbuf+数据的空间,之后需要判断申请的数据空间是否足够,如果不足够,需要以链表形式将多个pbuf链接。

注意:在数据空间中,只有第一个pbuf有报文头,但每个空间都有一个pbuf控制块。

  • PBUF_RAM

它是在内存堆里申请一个pbuf+数据空间的内存堆

PBUF_REF、PBUF_ROM函数实现:

/* 在pbuf_alloc中相关的代码 */
/* 该代码最终调用pbuf_alloc_reference */
case PBUF_REF: /* fall through */
case PBUF_ROM:p = pbuf_alloc_reference(NULL, length, type);break;/* pbuf_alloc_reference的代码 */
struct pbuf *
pbuf_alloc_reference(void *payload, u16_t length, pbuf_type type)
{struct pbuf *p;LWIP_ASSERT("invalid pbuf_type", (type == PBUF_REF) || (type == PBUF_ROM));//在MEMP_PBUF内存池中申请空间p = (struct pbuf *)memp_malloc(MEMP_PBUF);    if (p == NULL) {LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,("pbuf_alloc_reference: Could not allocate MEMP_PBUF for PBUF_%s.\n",(type == PBUF_ROM) ? "ROM" : "REF"));return NULL;}//初始化pbuf,注意这里的payload=NULL,因为该类型中不包含数据pbuf_init_alloced_pbuf(p, payload, length, length, type, 0);return p;
}

 PBUF_POOL​​​​​​​函数实现:

case PBUF_POOL: {//q为新的pbuf,last为最后一个pbuf,p为pbuf的头//申请空间后,以尾插法将新节点连接struct pbuf *q, *last;u16_t rem_len; /* remaining length */p = NULL; last = NULL;rem_len = length;do {u16_t qlen;//申请内存池,该内存池的大小 = pbuf大小+PBUF_POOL_BUFSIZE_ALIGNED(数据空间)q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);	if (q == NULL) {PBUF_POOL_IS_EMPTY();/* free chain so far allocated */if (p) {pbuf_free(p);}/* bail out unsuccessfully */return NULL;}//取数据长度与可用数据空间的最小值,可用空间 = 总数据空间 - 报文头空间//PBUF_POOL_BUFSIZE_ALIGNED是总数据空间,pbuf的空间已经被减去了,因此该计算值是数据可用的空间qlen = LWIP_MIN(rem_len, (u16_t)(PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)));//初始化pbuf管理的空间,payload偏移 pbuf大小 + 报文头大小pbuf_init_alloced_pbuf(q, LWIP_MEM_ALIGN((void *)((u8_t *)q + SIZEOF_STRUCT_PBUF + offset)),rem_len, qlen, type, 0);LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned",((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0);LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT",(PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 );if (p == NULL) {/* allocated head of pbuf chain (into p) */p = q;} else {/* make previous pbuf point to this pbuf */last->next = q;}last = q;rem_len = (u16_t)(rem_len - qlen);offset = 0;} while (rem_len > 0);break;}

 PBUF_RAM函数实现:

case PBUF_RAM: {mem_size_t payload_len = (mem_size_t)(LWIP_MEM_ALIGN_SIZE(offset) + LWIP_MEM_ALIGN_SIZE(length));mem_size_t alloc_len = (mem_size_t)(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF) + payload_len);/* bug #50040: Check for integer overflow when calculating alloc_len */if ((payload_len < LWIP_MEM_ALIGN_SIZE(length)) ||(alloc_len < LWIP_MEM_ALIGN_SIZE(length))) {return NULL;}//在内存堆中申请空间p = (struct pbuf *)mem_malloc(alloc_len);if (p == NULL) {return NULL;}//初始化pbufpbuf_init_alloced_pbuf(p, LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)),length, length, type, 0);LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned",((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);break;}

pbuf_alloc完整函数:

struct pbuf *
pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
{struct pbuf *p;u16_t offset = (u16_t)layer;LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length));//根据类型,申请对应的pbuf空间switch (type) {case PBUF_REF: /* fall through */case PBUF_ROM:p = pbuf_alloc_reference(NULL, length, type);break;case PBUF_POOL: {struct pbuf *q, *last;//q为新的pbuf,last为最后一个pbufu16_t rem_len; /* remaining length */p = NULL; //p为pbuf的头last = NULL;rem_len = length;do {u16_t qlen;//申请内存池,该内存池的大小 = pbuf大小+PBUF_POOL_BUFSIZE_ALIGNED(数据空间)q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);	if (q == NULL) {PBUF_POOL_IS_EMPTY();/* free chain so far allocated */if (p) {pbuf_free(p);}/* bail out unsuccessfully */return NULL;}//取数据长度与可用数据空间的最小值,可用空间 = 总数据空间 - 报文头空间//PBUF_POOL_BUFSIZE_ALIGNED是总数据空间,pbuf的空间已经被减去了,因此该计算值是数据可用的空间qlen = LWIP_MIN(rem_len, (u16_t)(PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)));//初始化pbuf管理的空间,payload偏移 pbuf大小 + 报文头大小pbuf_init_alloced_pbuf(q, LWIP_MEM_ALIGN((void *)((u8_t *)q + SIZEOF_STRUCT_PBUF + offset)),rem_len, qlen, type, 0);LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned",((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0);LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT",(PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 );if (p == NULL) {/* allocated head of pbuf chain (into p) */p = q;} else {/* make previous pbuf point to this pbuf */last->next = q;}last = q;rem_len = (u16_t)(rem_len - qlen);offset = 0;} while (rem_len > 0);break;}case PBUF_RAM: {mem_size_t payload_len = (mem_size_t)(LWIP_MEM_ALIGN_SIZE(offset) + LWIP_MEM_ALIGN_SIZE(length));mem_size_t alloc_len = (mem_size_t)(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF) + payload_len);/* bug #50040: Check for integer overflow when calculating alloc_len */if ((payload_len < LWIP_MEM_ALIGN_SIZE(length)) ||(alloc_len < LWIP_MEM_ALIGN_SIZE(length))) {return NULL;}/* If pbuf is to be allocated in RAM, allocate memory for it. */p = (struct pbuf *)mem_malloc(alloc_len);if (p == NULL) {return NULL;}pbuf_init_alloced_pbuf(p, LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)),length, length, type, 0);LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned",((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);break;}default:LWIP_ASSERT("pbuf_alloc: erroneous type", 0);return NULL;}LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p));return p;
}

2、pbuf_free

函数功能:

释放pbuf内存

函数结构:

以链表遍历的形式遍历并释放pbuf

释放时,首先通过ref判断pbuf是否在其他地方被使用,只有未被使用的pbuf才能被释放

之后获得pbuf的类型,并根据pbuf的类型进行相应操作的释放

函数实现:

u8_t
pbuf_free(struct pbuf *p)
{u8_t alloc_src;struct pbuf *q;u8_t count;if (p == NULL) {LWIP_ASSERT("p != NULL", p != NULL);/* if assertions are disabled, proceed with debug output */LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,("pbuf_free(p == NULL) was called.\n"));return 0;}LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p));PERF_START;count = 0;/* de-allocate all consecutive pbufs from the head of the chain that* obtain a zero reference count after decrementing*///以链表遍历的情况进行释放while (p != NULL) {LWIP_PBUF_REF_T ref;SYS_ARCH_DECL_PROTECT(old_level);/* Since decrementing ref cannot be guaranteed to be a single machine operation* we must protect it. We put the new ref into a local variable to prevent* further protection. */SYS_ARCH_PROTECT(old_level);/* all pbufs in a chain are referenced at least once */LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0);/* decrease reference count (number of pointers to pbuf) *///初始化时ref=1,之后if (ref == 0)是为了确保pbuf未在其他地方使用ref = --(p->ref);SYS_ARCH_UNPROTECT(old_level);/* this pbuf is no longer referenced to? */if (ref == 0) {/* remember next pbuf in chain for next iteration */q = p->next;LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p));//获取到pbuf的类型alloc_src = pbuf_get_allocsrc(p);
#if LWIP_SUPPORT_CUSTOM_PBUF/* is this a custom pbuf? */if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) {struct pbuf_custom *pc = (struct pbuf_custom *)p;LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL);pc->custom_free_function(p);} else
#endif /* LWIP_SUPPORT_CUSTOM_PBUF */{//根据pbuf的类型进行相应的释放操作/* is this a pbuf from the pool? */if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF_POOL) {memp_free(MEMP_PBUF_POOL, p);/* is this a ROM or RAM referencing pbuf? */} else if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF) {memp_free(MEMP_PBUF, p);/* type == PBUF_RAM */} else if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP) {mem_free(p);} else {/* @todo: support freeing other types */LWIP_ASSERT("invalid pbuf type", 0);}}count++;/* proceed to next pbuf */p = q;/* p->ref > 0, this pbuf is still referenced to *//* (and so the remaining pbufs in chain as well) */} else {LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, (u16_t)ref));/* stop walking through the chain */p = NULL;}}PERF_STOP("pbuf_free");/* return number of de-allocated pbufs */return count;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com