LWIP学习
转自:https://blog.csdn.net/kzq_qmi/article/details/46900589
数据包pbuf:
LwIP采用数据结构 pbuf 来描述数据包,其结构如下:
struct pbuf {
/** next pbuf in singly linked pbuf chain */
struct pbuf *next;
/** pointer to the actual data in the buffer */
void *payload;
/**
* total length of this buffer and all next buffers in chain
* belonging to the same packet.
*
* For non-queue packet chains this is the invariant:
* p->tot_len == p->len + (p->next? p->next->tot_len: 0)
*/
u16_t tot_len;
/** length of this buffer */
u16_t len;
/** pbuf_type as u8_t instead of enum to save space */
u8_t /*pbuf_type*/ type;
/** misc flags */
u8_t flags;
/**
* the reference count always equals the number of pointers
* that refer to this pbuf. This can be pointers from an application,
* the stack itself, or pbuf->next pointers from a chain.
*/
u16_t ref;
};
各成员含义上面的注释已经说得很清楚了。
关于采用链表结构,是因为实际发送或接收的数据包可能很大,而每个 pbuf 能够管理的数据可能很少,所以,往往需要多个 pbuf 结构才能完全描述一个数据包。
另外,最后的 ref 字段表示该 pbuf 被引用的次数。这里又是一个纠结的地方啊。初始化一个 pbuf 的时候, ref 字段值被设置为 1,当有其他 pbuf 的 next 指针指向该 pbuf 时,该 pbuf 的 ref 字段值加一。所以,要删除一个 pbuf 时, ref 的值必须为 1 才能删除成功,否则删除失败。
上图中注意 payload 并没有指向 ref 字段之后,而是隔了一定的区域。这段区域就是offset 的大小,这段区域用来存储数据的包头,如 TCP 包头, IP 包头等。当然, offset 也可以是 0。
来看代码:
/**
* Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type).
*
* The actual memory allocated for the pbuf is determined by the
* layer at which the pbuf is allocated and the requested size
* (from the size parameter).
*
* @param layer flag to define header size
* @param length size of the pbuf's payload
* @param type this parameter decides how and where the pbuf
* should be allocated as follows:
*
* - PBUF_RAM: buffer memory for pbuf is allocated as one large
* chunk. This includes protocol headers as well.
* - PBUF_ROM: no buffer memory is allocated for the pbuf, even for
* protocol headers. Additional headers must be prepended
* by allocating another pbuf and chain in to the front of
* the ROM pbuf. It is assumed that the memory used is really
* similar to ROM in that it is immutable and will not be
* changed. Memory which is dynamic should generally not
* be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
* - PBUF_REF: no buffer memory is allocated for the pbuf, even for
* protocol headers. It is assumed that the pbuf is only
* being used in a single thread. If the pbuf gets queued,
* then pbuf_take should be called to copy the buffer.
* - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from
* the pbuf pool that is allocated during pbuf_init().
*
* @return the allocated pbuf. If multiple pbufs where allocated, this
* is the first pbuf of a pbuf chain.
*/
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; /* remaining length */
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length));
/* determine header offset */
offset = 0;
switch (layer) { //注意这里从协议栈上层开始,方便offset从上层往下叠加,因此也没加 break
case PBUF_TRANSPORT:
/* add room for transport (often TCP) layer header */
offset += PBUF_TRANSPORT_HLEN;
/* FALLTHROUGH */
case PBUF_IP:
/* add room for IP layer header */
offset += PBUF_IP_HLEN;
/* FALLTHROUGH */
case PBUF_LINK:
/* add room for link layer header */
offset += PBUF_LINK_HLEN;
break;
case PBUF_RAW:
break;
default:
LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0);
return NULL;
}
switch (type) {
case PBUF_POOL:
/* allocate head of pbuf chain into p */
p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); //分配第一个pbuf
if (p == NULL) {
return NULL;
}
p->type = type;
p->next = NULL;
/* make the payload pointer point 'offset' bytes into pbuf data memory */
p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset)));
/* the total length of the pbuf chain is the requested size */
p->tot_len = length; //该pbuf及其以后pbuf的负载数据总长度
/* set the length of the first pbuf in the chain */
p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)); //负载数据可能大于分配空间长度,也有可能小于,取当前pbuf实际的负载长度
/* set reference count (needed here in case we fail) */
p->ref = 1;
/* now allocate the tail of the pbuf chain */
//如果一个pbuf不够的话,接着分配
/* remember first pbuf for linkage in next iteration */
r = p;
/* remaining length to be allocated */
rem_len = length - p->len;
/* any remaining pbufs to be allocated? */
while (rem_len > 0) {
q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); //从第二个pbuf开始,不再需要TCP/IP之类的头,所以没有offset
if (q == NULL) {
/* free chain so far allocated */
pbuf_free(p); //注意这里,如果当前pbuf分配不成功,要把之前分配的所有pbuf都释放掉
/* bail out unsuccesfully */
return NULL;
}
q->type = type;
q->flags = 0;
q->next = NULL;
/* make previous pbuf point to this pbuf */
r->next = q;
/* set total length of this pbuf and next in chain */
q->tot_len = (u16_t)rem_len;
/* this pbuf length is pool size, unless smaller sized tail */
q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED);
q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF);
q->ref = 1;
/* calculate remaining length to be allocated */
rem_len -= q->len;
/* remember this pbuf for linkage in next iteration */
r = q;
}
/* end of chain */
/*r->next = NULL;*/
break;
case PBUF_RAM:
/* If pbuf is to be allocated in RAM, allocate memory for it. */
p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));
if (p == NULL) {
return NULL;
}
/* Set up internal structure of the pbuf. */
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;
/* pbuf references existing (non-volatile static constant) ROM payload? */
case PBUF_ROM:
/* pbuf references existing (externally allocated) RAM payload? */
case PBUF_REF:
/* only allocate memory for the pbuf structure */
p = (struct pbuf *)memp_malloc(MEMP_PBUF);
if (p == NULL) {
return NULL;
}
/* caller must set this field properly, afterwards */
p->payload = NULL;
p->len = p->tot_len = length;
p->next = NULL;
p->type = type;
break;
default:
return NULL;
}
/* set reference count */
p->ref = 1;
/* set flags */
p->flags = 0;
return p;
}
/**
* Dereference a pbuf chain or queue and deallocate any no-longer-used
* pbufs at the head of this chain or queue.
*
* Decrements the pbuf reference count. If it reaches zero, the pbuf is
* deallocated.
*
* For a pbuf chain, this is repeated for each pbuf in the chain,
* up to the first pbuf which has a non-zero reference count after
* decrementing. So, when all reference counts are one, the whole
* chain is free'd.
*
* @param p The pbuf (chain) to be dereferenced.
*
* @return the number of pbufs that were de-allocated
* from the head of the chain.
*
* @note MUST NOT be called on a packet queue (Not verified to work yet).
* @note the reference counter of a pbuf equals the number of pointers
* that refer to the pbuf (or into the pbuf).
*
* @internal examples:
*
* Assuming existing chains a->b->c with the following reference
* counts, calling pbuf_free(a) results in:
*
* 1->2->3 becomes ...1->3
* 3->3->3 becomes 2->3->3
* 1->1->2 becomes ......1
* 2->1->1 becomes 1->1->1
* 1->1->1 becomes .......
*
*/
u8_t
pbuf_free(struct pbuf *p)
{
u16_t type;
struct pbuf *q;
u8_t count;
if (p == NULL) {
return 0;
}
count = 0;
/* de-allocate all consecutive pbufs from the head of the chain that
* obtain a zero reference count after decrementing*/
while (p != NULL) {
u16_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 = --(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;
type = p->type;
/* is this a pbuf from the pool? */
if (type == PBUF_POOL) {
memp_free(MEMP_PBUF_POOL, p);
/* is this a ROM or RAM referencing pbuf? */
} else if (type == PBUF_ROM || type == PBUF_REF) {
memp_free(MEMP_PBUF, p);
/* type == PBUF_RAM */
} else {
mem_free(p);
}
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 {
/* stop walking through the chain */
p = NULL;
}
}
/* return number of de-allocated pbufs */
return count;
}
/**
*
* Create PBUF_RAM copies of pbufs.
*
* Used to queue packets on behalf of the lwIP stack, such as
* ARP based queueing.
*
* @note You MUST explicitly use p = pbuf_take(p);
*
* @note Only one packet is copied, no packet queue!
*
* @param p_to pbuf destination of the copy
* @param p_from pbuf source of the copy
*
* @return ERR_OK if pbuf was copied
* ERR_ARG if one of the pbufs is NULL or p_to is not big
* enough to hold p_from
*/
err_t
pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
{
u16_t offset_to=0, offset_from=0, len;
/* is the target big enough to hold the source? */
LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) &&
(p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;);
/* iterate through pbuf chain */
do
{
LWIP_ASSERT("p_to != NULL", p_to != NULL);
/* copy one part of the original chain */
if ((p_to->len - offset_to) >= (p_from->len - offset_from)) { //每次拷贝的长度是源端和目标端当前pbuf所剩空间的较小值,offset为当前pbuf拷贝数据的偏移量
/* complete current p_from fits into current p_to */
len = p_from->len - offset_from;
} else {
/* current p_from does not fit into current p_to */
len = p_to->len - offset_to;
}
MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len);
offset_to += len;
offset_from += len;
if (offset_to == p_to->len) { //目标端当前pbuf空间已满,转向下一个pbuf,记得offset清零
/* on to next p_to (if any) */
offset_to = 0;
p_to = p_to->next;
}
if (offset_from >= p_from->len) { //源端当前pbuf数据已拷贝完,转向下一个pbuf,记得offset清零
/* on to next p_from (if any) */
offset_from = 0;
p_from = p_from->next;
}
if((p_from != NULL) && (p_from->len == p_from->tot_len)) {
/* don't copy more than one packet! */
LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
(p_from->next == NULL), return ERR_VAL;);
}
if((p_to != NULL) && (p_to->len == p_to->tot_len)) {
/* don't copy more than one packet! */
LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
(p_to->next == NULL), return ERR_VAL;);
}
} while (p_from);
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n"));
return ERR_OK;
}
可以看到,回收 pbuf 使用pbuf_free()函数,该函数首先要减少 pbuf 索引计数(reference count)。如果引用计数已经减为 0,这个 pbuf 被回收。对于一个pbuf链来说,只有前一个pbuf被回收,才会考虑回收后面的pbuf,如果前面pbuf计数还不为0,则直接返回。
LWIP学习的更多相关文章
- 内存管理pbuf.c源码解析——LwIP学习
声明:个人所写所有博客均为自己在学习中的记录与感想,或为在学习中总结他人学习成果,但因本人才疏学浅,如果大家在阅读过程中发现错误,欢迎大家指正. 本文自己尚有认为写的不完整的地方,源代码没有完全理清, ...
- 内存管理pbuf.h头文件源码解析——LwIP学习
声明:个人所写所有博客均为自己在学习中的记录与感想,或为在学习中总结他人学习成果,但因本人才疏学浅,如果大家在阅读过程中发现错误,欢迎大家指正. LwIP的内核(core文件夹)文件中pbuf.c是包 ...
- LwIP学习笔记——STM32 ENC28J60移植与入门
0.前言 去年(2013年)的整理了LwIP相关代码,并在STM32上"裸奔"成功.一直没有时间深入整理,在这里借博文整理总结.LwIP的移植过程细节很多,博文也不可能一一 ...
- LWIP学习之一些细节
一 绑定端口后,开启监听,为何监听还要返回一个新的连接?:监听状态的连接只需要很小的内存,于是tcp_listen()就会收回原始连接的内存,而重新分配一个较小内存块供处于监听状态的连接使用. 二 t ...
- LWIP学习之流程架构
一 STM32F107的网络接口配置:#include "stm32_eth.h" 1.1 打开网口时钟,响应IO配置.NVIC中断:通过调用Ethernet_Configurat ...
- TCP/IP协议学习(二) LWIP用户自定义配置文件解析
LWIP协议支持用户配置,可以通过用户裁剪实现最优化配置,LWIP默认包含opts.h作为系统默认配置,不过通过添加lwipopts.h文件并包含在opts.h头文件之前就可以对lwip进行用户裁剪, ...
- TCP/IP协议学习(一) LWIP实现网络远程IAP下载更新
最近需要实现通过TCP/IP远程IAP在线更新功能,忙了2周终于在原有嵌入式服务器的基础上实现了该功能,这里就记录下实现的过程. IAP又称在应用编程,其实说简单点就是实现不需要jlink,仅通过芯片 ...
- lwip协议栈学习---udp
书籍:<嵌入式网络那些事-lwip协议> udp协议的优点: 1)基于IP协议,无连接的用户数据报协议,适用于传送大批量数据, 2)实时性比较高,适用于嵌入式网络 发送函数:udp_sen ...
- LwIP的SNMP学习笔记
关于这方面的资料网上非常少,做一下笔记. 在LwIP中,在\lwip-1.4.1\src\core\snmp目录下有SNMP相关的c文件,在lwip-1.4.1\src\include\lwip目录下 ...
随机推荐
- Docker之数据卷Volume(七)
一.简介 Docker数据卷(volume)机制.volume是存在于一个或多个容器中的特定文件或文件夹,这个目录以独立于联合文件系统的形式在宿主机中存在,并为数据的共享与持久化提供便利. 1)v ...
- gcc centos 新版本的安装方法
因为centos默认安装的gcc是GCC 4.*.* 是不支持 C++11 的,所以有些新的程序或软件要安装就行要升级GCC,否则无法编译通过 一.如下步骤安装不成功(yum install devt ...
- C#.NET XML报文签名与验签
-- MD5Util: using System; using System.Collections.Generic; using System.Security.Cryptography; usin ...
- I18nUtils
import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.*; ...
- Bootstrap 前端UI框架
Bootstrap 有哪些优越性? 1.简单灵活的用于搭建WEB页面的HTML,CSS, JavaScript的工具集 2.基于html5, css3, 具有良好特性,友好的学习曲线,卓越的兼容性,1 ...
- 学习MeteoInfo二次开发教程(七)
1.站点文件 12010615.syn在D:\Program Files (x86)\MeteoInfo\SampleSYNOP_Stations.csv在D:\Program Files (x86) ...
- mysql数据库优化(四)-项目实战
在flask项目中,防止随着时间的流逝,数据库数据越来越多,导致接口访问数据库速度变慢.所以自己填充数据进行测试及 mysql优化 1.插入数据: 通过脚本,使用多进程,每100次提交数据 impor ...
- WebService . Schema约束
1. namespace 相当于schema文件的id 2. targetNamespace属性 用来指定schema文件的namespace的值 3. xmlns属性 引入一个约束, 它的值是一个s ...
- Android 开发 VectorDrawable 矢量图 (三)矢量图动画
VectorDrawable 矢量图 三部曲: Android 开发 VectorDrawable 矢量图 (一)了解Android矢量图与获取矢量图 Android 开发 VectorDrawabl ...
- pyqt---------事件与信号处理
pyqt:信号与槽的关系 GUI应用程序是事件驱动的. 事件主要由应用程序的用户生成. 但它们也可以通过其他手段产生,例如:网络连接,窗口管理器或定时器. 当我们调用应用程序的exec_()方法时,应 ...