背景
TCP/IP 是一种数据通信机制,本质是对数据包进行处理。 链路层判断收到数据包类型,会提取数据字段,记录主机物理地址信息; IP层提取IP地址,实现数据的存储、转发,根据数据包编号实现数据包重装,提取数据包中关于传输层的信息,向传输层递交数据包并记录递交结果; 传输层中TCP使用数据包信息更新TCP状态机,并向应用程序递交数据。
数据包管理应该是一种高效管理机制,使协议栈各层对数据灵活处理同时还能减少数据在各层间传递时的时间和空间开销。 协议栈本是严格分层的,但是这会使层间数据递交非常慢,为避免这种情况,代码内部没有采用完整的分层结构,部分数据结构和实现原理再其他层是可见的。协议栈实现时,仍然是贯彻分层思想,每层都是独立模块实现,提供 输入输出函数。但是不是严格的分层机制,各层级存在交叉存取现象。 一般来讲,协议栈是设计为内核代码的一部分,与用户层保持着完全的分层封装。但是小型嵌入式设备中,内核空间和用户空间没有明显的分层现象,这就允许用户和内核之间有更多宽松的数据处理机制。
LWIP采用如下进程模型: 协议栈作为一个操作系统的独立进程,用户可以留驻在协议栈进程 中,也可以是一个独立的进程。第一种方式,用户与协议栈通信通过回调函数;第二种方式,用户和协议栈通信需要调用系统提供的信号量和邮箱机制。
PBUF结构体
设计PBUF包的核心是能容纳不同类型的数据,又能避免各层间数据拷贝。 结构体定义如下:
struct pbuf {
struct pbuf *next;
void *payload;
u16_t tot_len;
u16_t len;
u8_t type;
u8_t flags;
u16_t ref;
};
名称 | 描述 |
---|
next | 指向下一个pbuf数据结构 | payload | 数据起始地址 | tot_len | 当前和其后面所有pbuf有效数据总长度 | len | 有效数据长度 | type | pbuf数据类型 | flags | 没用到 | ref | 该pbuf被引用的次数 |
typedef enum {
PBUF_RAM,
PBUF_ROM,
PBUF_REF,
PBUF_POOL
} pbuf_type;
PBUF_RAM
PBUF_RAM类型是通过内存堆分配的,这种类型是协议栈使用最多的,协议栈中待发数据和应用程序的待发数据一般都采用这个形式。 下面是源代码申请这种类型:
p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));
分配空间大小有pbuf结构大小、数据存储空间大小、offset。分配成功后的数据结构如下图所示: 从图中可以看出,payload并非指向整个数据区的起始处,而是间隔了offset,这段长度内存通常是各种首部字段,如TCP报文首部、IP首部、以太网首部等。
PBUF_ROM
PBUF_ROM类型是内存池中分配一个相应的pbuf结构,不申请数据区的空间。发送一些静态数据,可以采用这种数据类型。
PBUF_REF
PBUF_REF同PBUF_ROM相同,内存池中分配一个相应的pbuf结构,不申请数据区的空间。
p = (struct pbuf *)memp_malloc(MEMP_PBUF);
PBUF_POOL
PBUF_POOL类型是通过内存池分配得到,下面是源代码申请这种类型:
p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
数据包管理
数据包申请函数pbuf_alloc(),有两个重要参数,一个是数据包pbuf类型,上面已经讲的很详细了;第二个是协议栈的层次,根据层次决定offset值不同。
struct pbuf * pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
{
struct pbuf *p, *q, *r;
u16_t offset;
s32_t rem_len;
offset = 0;
switch (layer) {
case PBUF_TRANSPORT:
offset += PBUF_TRANSPORT_HLEN;
case PBUF_IP:
offset += PBUF_IP_HLEN;
case PBUF_LINK:
offset += PBUF_LINK_HLEN;
break;
case PBUF_RAW:
break;
default:
return NULL;
}
switch (type) {
case PBUF_POOL:
p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
if (p == NULL) {
PBUF_POOL_IS_EMPTY();
return NULL;
}
p->type = type;
p->next = NULL;
p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset)));
p->tot_len = length;
p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset));
p->ref = 1;
r = p;
rem_len = length - p->len;
while (rem_len > 0) {
q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
if (q == NULL) {
PBUF_POOL_IS_EMPTY();
pbuf_free(p);
return NULL;
}
q->type = type;
q->flags = 0;
q->next = NULL;
r->next = q;
q->tot_len = (u16_t)rem_len;
q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED);
q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF);
q->ref = 1;
rem_len -= q->len;
r = q;
}
break;
case PBUF_RAM:
p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));
if (p == NULL) {
return NULL;
}
p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset));
p->len = p->tot_len = length;
p->next = NULL;
p->type = type;
break;
case PBUF_ROM:
case PBUF_REF:
p = (struct pbuf *)memp_malloc(MEMP_PBUF);
if (p == NULL) {
return NULL;
}
p->payload = NULL;
p->len = p->tot_len = length;
p->next = NULL;
p->type = type;
break;
default:
return NULL;
}
p->ref = 1;
p->flags = 0;
return p;
}
数据包释放函数pbuf_free(),需要注意的是能被删除的节点一定是pbuf链表的首节点或其他地方引用过的节点。
u8_t pbuf_free(struct pbuf *p)
{
u16_t type;
struct pbuf *q;
u8_t count;
if (p == NULL) {
return 0;
}
PERF_START;
count = 0;
while (p != NULL) {
u16_t ref;
if (ref == 0) {
q = p->next;
type = p->type;
#if LWIP_SUPPORT_CUSTOM_PBUF
if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) {
struct pbuf_custom *pc = (struct pbuf_custom*)p;
pc->custom_free_function(p);
} else
#endif
{
if (type == PBUF_POOL) {
memp_free(MEMP_PBUF_POOL, p);
} else if (type == PBUF_ROM || type == PBUF_REF) {
memp_free(MEMP_PBUF, p);
} else {
mem_free(p);
}
}
count++;
p = q;
} else {
p = NULL;
}
}
PERF_STOP("pbuf_free");
return count;
}
当删除某个pbuf结构时,会检测其类型,根据类型调用不同的释放函数释放。
|