转自:https://blog.csdn.net/kzq_qmi/article/details/46900589

数据包pbuf: 
   
  LwIP采用数据结构 pbuf 来描述数据包,其结构如下: 
   
  

  1. struct pbuf {
  2. /** next pbuf in singly linked pbuf chain */
  3. struct pbuf *next;
  4. /** pointer to the actual data in the buffer */
  5. void *payload;
  6. /**
  7. * total length of this buffer and all next buffers in chain
  8. * belonging to the same packet.
  9. *
  10. * For non-queue packet chains this is the invariant:
  11. * p->tot_len == p->len + (p->next? p->next->tot_len: 0)
  12. */
  13. u16_t tot_len;
  14. /** length of this buffer */
  15. u16_t len;
  16. /** pbuf_type as u8_t instead of enum to save space */
  17. u8_t /*pbuf_type*/ type;
  18. /** misc flags */
  19. u8_t flags;
  20. /**
  21. * the reference count always equals the number of pointers
  22. * that refer to this pbuf. This can be pointers from an application,
  23. * the stack itself, or pbuf->next pointers from a chain.
  24. */
  25. u16_t ref;
  26. };

  各成员含义上面的注释已经说得很清楚了。 
  关于采用链表结构,是因为实际发送或接收的数据包可能很大,而每个 pbuf 能够管理的数据可能很少,所以,往往需要多个 pbuf 结构才能完全描述一个数据包。 
  另外,最后的 ref 字段表示该 pbuf 被引用的次数。这里又是一个纠结的地方啊。初始化一个 pbuf 的时候, ref 字段值被设置为 1,当有其他 pbuf 的 next 指针指向该 pbuf 时,该 pbuf 的 ref 字段值加一。所以,要删除一个 pbuf 时, ref 的值必须为 1 才能删除成功,否则删除失败。 
  上图中注意 payload 并没有指向 ref 字段之后,而是隔了一定的区域。这段区域就是offset 的大小,这段区域用来存储数据的包头,如 TCP 包头, IP 包头等。当然, offset 也可以是 0。

来看代码:

  1. /**
  2. * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type).
  3. *
  4. * The actual memory allocated for the pbuf is determined by the
  5. * layer at which the pbuf is allocated and the requested size
  6. * (from the size parameter).
  7. *
  8. * @param layer flag to define header size
  9. * @param length size of the pbuf's payload
  10. * @param type this parameter decides how and where the pbuf
  11. * should be allocated as follows:
  12. *
  13. * - PBUF_RAM: buffer memory for pbuf is allocated as one large
  14. * chunk. This includes protocol headers as well.
  15. * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for
  16. * protocol headers. Additional headers must be prepended
  17. * by allocating another pbuf and chain in to the front of
  18. * the ROM pbuf. It is assumed that the memory used is really
  19. * similar to ROM in that it is immutable and will not be
  20. * changed. Memory which is dynamic should generally not
  21. * be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
  22. * - PBUF_REF: no buffer memory is allocated for the pbuf, even for
  23. * protocol headers. It is assumed that the pbuf is only
  24. * being used in a single thread. If the pbuf gets queued,
  25. * then pbuf_take should be called to copy the buffer.
  26. * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from
  27. * the pbuf pool that is allocated during pbuf_init().
  28. *
  29. * @return the allocated pbuf. If multiple pbufs where allocated, this
  30. * is the first pbuf of a pbuf chain.
  31. */
  32. struct pbuf *
  33. pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
  34. {
  35. struct pbuf *p, *q, *r;
  36. u16_t offset;
  37. s32_t rem_len; /* remaining length */
  38. LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length));
  39. /* determine header offset */
  40. offset = 0;
  41. switch (layer) { //注意这里从协议栈上层开始,方便offset从上层往下叠加,因此也没加 break
  42. case PBUF_TRANSPORT:
  43. /* add room for transport (often TCP) layer header */
  44. offset += PBUF_TRANSPORT_HLEN;
  45. /* FALLTHROUGH */
  46. case PBUF_IP:
  47. /* add room for IP layer header */
  48. offset += PBUF_IP_HLEN;
  49. /* FALLTHROUGH */
  50. case PBUF_LINK:
  51. /* add room for link layer header */
  52. offset += PBUF_LINK_HLEN;
  53. break;
  54. case PBUF_RAW:
  55. break;
  56. default:
  57. LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0);
  58. return NULL;
  59. }
  60. switch (type) {
  61. case PBUF_POOL:
  62. /* allocate head of pbuf chain into p */
  63. p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); //分配第一个pbuf
  64. if (p == NULL) {
  65. return NULL;
  66. }
  67. p->type = type;
  68. p->next = NULL;
  69. /* make the payload pointer point 'offset' bytes into pbuf data memory */
  70. p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset)));
  71. /* the total length of the pbuf chain is the requested size */
  72. p->tot_len = length; //该pbuf及其以后pbuf的负载数据总长度
  73. /* set the length of the first pbuf in the chain */
  74. p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)); //负载数据可能大于分配空间长度,也有可能小于,取当前pbuf实际的负载长度
  75. /* set reference count (needed here in case we fail) */
  76. p->ref = 1;
  77. /* now allocate the tail of the pbuf chain */
  78. //如果一个pbuf不够的话,接着分配
  79. /* remember first pbuf for linkage in next iteration */
  80. r = p;
  81. /* remaining length to be allocated */
  82. rem_len = length - p->len;
  83. /* any remaining pbufs to be allocated? */
  84. while (rem_len > 0) {
  85. q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); //从第二个pbuf开始,不再需要TCP/IP之类的头,所以没有offset
  86. if (q == NULL) {
  87. /* free chain so far allocated */
  88. pbuf_free(p); //注意这里,如果当前pbuf分配不成功,要把之前分配的所有pbuf都释放掉
  89. /* bail out unsuccesfully */
  90. return NULL;
  91. }
  92. q->type = type;
  93. q->flags = 0;
  94. q->next = NULL;
  95. /* make previous pbuf point to this pbuf */
  96. r->next = q;
  97. /* set total length of this pbuf and next in chain */
  98. q->tot_len = (u16_t)rem_len;
  99. /* this pbuf length is pool size, unless smaller sized tail */
  100. q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED);
  101. q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF);
  102. q->ref = 1;
  103. /* calculate remaining length to be allocated */
  104. rem_len -= q->len;
  105. /* remember this pbuf for linkage in next iteration */
  106. r = q;
  107. }
  108. /* end of chain */
  109. /*r->next = NULL;*/
  110. break;
  111. case PBUF_RAM:
  112. /* If pbuf is to be allocated in RAM, allocate memory for it. */
  113. p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));
  114. if (p == NULL) {
  115. return NULL;
  116. }
  117. /* Set up internal structure of the pbuf. */
  118. p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset));
  119. p->len = p->tot_len = length;
  120. p->next = NULL;
  121. p->type = type;
  122. break;
  123. /* pbuf references existing (non-volatile static constant) ROM payload? */
  124. case PBUF_ROM:
  125. /* pbuf references existing (externally allocated) RAM payload? */
  126. case PBUF_REF:
  127. /* only allocate memory for the pbuf structure */
  128. p = (struct pbuf *)memp_malloc(MEMP_PBUF);
  129. if (p == NULL) {
  130. return NULL;
  131. }
  132. /* caller must set this field properly, afterwards */
  133. p->payload = NULL;
  134. p->len = p->tot_len = length;
  135. p->next = NULL;
  136. p->type = type;
  137. break;
  138. default:
  139. return NULL;
  140. }
  141. /* set reference count */
  142. p->ref = 1;
  143. /* set flags */
  144. p->flags = 0;
  145. return p;
  146. }
  147. /**
  148. * Dereference a pbuf chain or queue and deallocate any no-longer-used
  149. * pbufs at the head of this chain or queue.
  150. *
  151. * Decrements the pbuf reference count. If it reaches zero, the pbuf is
  152. * deallocated.
  153. *
  154. * For a pbuf chain, this is repeated for each pbuf in the chain,
  155. * up to the first pbuf which has a non-zero reference count after
  156. * decrementing. So, when all reference counts are one, the whole
  157. * chain is free'd.
  158. *
  159. * @param p The pbuf (chain) to be dereferenced.
  160. *
  161. * @return the number of pbufs that were de-allocated
  162. * from the head of the chain.
  163. *
  164. * @note MUST NOT be called on a packet queue (Not verified to work yet).
  165. * @note the reference counter of a pbuf equals the number of pointers
  166. * that refer to the pbuf (or into the pbuf).
  167. *
  168. * @internal examples:
  169. *
  170. * Assuming existing chains a->b->c with the following reference
  171. * counts, calling pbuf_free(a) results in:
  172. *
  173. * 1->2->3 becomes ...1->3
  174. * 3->3->3 becomes 2->3->3
  175. * 1->1->2 becomes ......1
  176. * 2->1->1 becomes 1->1->1
  177. * 1->1->1 becomes .......
  178. *
  179. */
  180. u8_t
  181. pbuf_free(struct pbuf *p)
  182. {
  183. u16_t type;
  184. struct pbuf *q;
  185. u8_t count;
  186. if (p == NULL) {
  187. return 0;
  188. }
  189. count = 0;
  190. /* de-allocate all consecutive pbufs from the head of the chain that
  191. * obtain a zero reference count after decrementing*/
  192. while (p != NULL) {
  193. u16_t ref;
  194. SYS_ARCH_DECL_PROTECT(old_level); //申请临界变量保护
  195. /* Since decrementing ref cannot be guaranteed to be a single machine operation
  196. * we must protect it. We put the new ref into a local variable to prevent
  197. * further protection. */
  198. SYS_ARCH_PROTECT(old_level); //进入临界区
  199. /* all pbufs in a chain are referenced at least once */
  200. LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0);
  201. /* decrease reference count (number of pointers to pbuf) */
  202. ref = --(p->ref);
  203. SYS_ARCH_UNPROTECT(old_level); //退出临界区
  204. /* this pbuf is no longer referenced to? */
  205. if (ref == 0) {
  206. /* remember next pbuf in chain for next iteration */
  207. q = p->next;
  208. type = p->type;
  209. /* is this a pbuf from the pool? */
  210. if (type == PBUF_POOL) {
  211. memp_free(MEMP_PBUF_POOL, p);
  212. /* is this a ROM or RAM referencing pbuf? */
  213. } else if (type == PBUF_ROM || type == PBUF_REF) {
  214. memp_free(MEMP_PBUF, p);
  215. /* type == PBUF_RAM */
  216. } else {
  217. mem_free(p);
  218. }
  219. count++;
  220. /* proceed to next pbuf */
  221. p = q;
  222. /* p->ref > 0, this pbuf is still referenced to */
  223. /* (and so the remaining pbufs in chain as well) */
  224. } else {
  225. /* stop walking through the chain */
  226. p = NULL;
  227. }
  228. }
  229. /* return number of de-allocated pbufs */
  230. return count;
  231. }
  232. /**
  233. *
  234. * Create PBUF_RAM copies of pbufs.
  235. *
  236. * Used to queue packets on behalf of the lwIP stack, such as
  237. * ARP based queueing.
  238. *
  239. * @note You MUST explicitly use p = pbuf_take(p);
  240. *
  241. * @note Only one packet is copied, no packet queue!
  242. *
  243. * @param p_to pbuf destination of the copy
  244. * @param p_from pbuf source of the copy
  245. *
  246. * @return ERR_OK if pbuf was copied
  247. * ERR_ARG if one of the pbufs is NULL or p_to is not big
  248. * enough to hold p_from
  249. */
  250. err_t
  251. pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
  252. {
  253. u16_t offset_to=0, offset_from=0, len;
  254. /* is the target big enough to hold the source? */
  255. LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) &&
  256. (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;);
  257. /* iterate through pbuf chain */
  258. do
  259. {
  260. LWIP_ASSERT("p_to != NULL", p_to != NULL);
  261. /* copy one part of the original chain */
  262. if ((p_to->len - offset_to) >= (p_from->len - offset_from)) { //每次拷贝的长度是源端和目标端当前pbuf所剩空间的较小值,offset为当前pbuf拷贝数据的偏移量
  263. /* complete current p_from fits into current p_to */
  264. len = p_from->len - offset_from;
  265. } else {
  266. /* current p_from does not fit into current p_to */
  267. len = p_to->len - offset_to;
  268. }
  269. MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len);
  270. offset_to += len;
  271. offset_from += len;
  272. if (offset_to == p_to->len) { //目标端当前pbuf空间已满,转向下一个pbuf,记得offset清零
  273. /* on to next p_to (if any) */
  274. offset_to = 0;
  275. p_to = p_to->next;
  276. }
  277. if (offset_from >= p_from->len) { //源端当前pbuf数据已拷贝完,转向下一个pbuf,记得offset清零
  278. /* on to next p_from (if any) */
  279. offset_from = 0;
  280. p_from = p_from->next;
  281. }
  282. if((p_from != NULL) && (p_from->len == p_from->tot_len)) {
  283. /* don't copy more than one packet! */
  284. LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
  285. (p_from->next == NULL), return ERR_VAL;);
  286. }
  287. if((p_to != NULL) && (p_to->len == p_to->tot_len)) {
  288. /* don't copy more than one packet! */
  289. LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
  290. (p_to->next == NULL), return ERR_VAL;);
  291. }
  292. } while (p_from);
  293. LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n"));
  294. return ERR_OK;
  295. }

  可以看到,回收 pbuf 使用pbuf_free()函数,该函数首先要减少 pbuf 索引计数(reference count)。如果引用计数已经减为 0,这个 pbuf 被回收。对于一个pbuf链来说,只有前一个pbuf被回收,才会考虑回收后面的pbuf,如果前面pbuf计数还不为0,则直接返回。

LWIP学习的更多相关文章

  1. 内存管理pbuf.c源码解析——LwIP学习

    声明:个人所写所有博客均为自己在学习中的记录与感想,或为在学习中总结他人学习成果,但因本人才疏学浅,如果大家在阅读过程中发现错误,欢迎大家指正. 本文自己尚有认为写的不完整的地方,源代码没有完全理清, ...

  2. 内存管理pbuf.h头文件源码解析——LwIP学习

    声明:个人所写所有博客均为自己在学习中的记录与感想,或为在学习中总结他人学习成果,但因本人才疏学浅,如果大家在阅读过程中发现错误,欢迎大家指正. LwIP的内核(core文件夹)文件中pbuf.c是包 ...

  3. LwIP学习笔记——STM32 ENC28J60移植与入门

    0.前言     去年(2013年)的整理了LwIP相关代码,并在STM32上"裸奔"成功.一直没有时间深入整理,在这里借博文整理总结.LwIP的移植过程细节很多,博文也不可能一一 ...

  4. LWIP学习之一些细节

    一 绑定端口后,开启监听,为何监听还要返回一个新的连接?:监听状态的连接只需要很小的内存,于是tcp_listen()就会收回原始连接的内存,而重新分配一个较小内存块供处于监听状态的连接使用. 二 t ...

  5. LWIP学习之流程架构

    一 STM32F107的网络接口配置:#include "stm32_eth.h" 1.1 打开网口时钟,响应IO配置.NVIC中断:通过调用Ethernet_Configurat ...

  6. TCP/IP协议学习(二) LWIP用户自定义配置文件解析

    LWIP协议支持用户配置,可以通过用户裁剪实现最优化配置,LWIP默认包含opts.h作为系统默认配置,不过通过添加lwipopts.h文件并包含在opts.h头文件之前就可以对lwip进行用户裁剪, ...

  7. TCP/IP协议学习(一) LWIP实现网络远程IAP下载更新

    最近需要实现通过TCP/IP远程IAP在线更新功能,忙了2周终于在原有嵌入式服务器的基础上实现了该功能,这里就记录下实现的过程. IAP又称在应用编程,其实说简单点就是实现不需要jlink,仅通过芯片 ...

  8. lwip协议栈学习---udp

    书籍:<嵌入式网络那些事-lwip协议> udp协议的优点: 1)基于IP协议,无连接的用户数据报协议,适用于传送大批量数据, 2)实时性比较高,适用于嵌入式网络 发送函数:udp_sen ...

  9. LwIP的SNMP学习笔记

    关于这方面的资料网上非常少,做一下笔记. 在LwIP中,在\lwip-1.4.1\src\core\snmp目录下有SNMP相关的c文件,在lwip-1.4.1\src\include\lwip目录下 ...

随机推荐

  1. Postgresql导出数据报版本不对

    zabbix使用得数据库是Postgresql,最近zabbix4.0版本出来了,准备把zabbix升级,得先把数据库备份,但是一直报错,如下:     查找服务器上是否有10的版本,也一直没找到   ...

  2. 【C++】vector用法详解

    转自:https://blog.csdn.net/fanyun_01/article/details/56842637#commentBox 一.简介 C++ vector类为内置数组提供了一种替代表 ...

  3. TCP/IP详解--TCP连接中TIME_WAIT状态过多

    TIMEWAIT状态本身和应用层的客户端或者服务器是没有关系的.仅仅是主动关闭的一方,在使用FIN|ACK|FIN|ACK四分组正常关闭TCP连接的时候会出现这个TIMEWAIT.服务器在处理客户端请 ...

  4. NodeJS对象数组Array 根据对象object key的值排序sort

    有个js对象数组 var ary=[{id:1,name:”b”},{id:2,name:”b”}] 需求是根据name 或者 id的值来排序,这里有个风骚的函数. /** * 对数组中的对象,按对象 ...

  5. 静态初始化块和main方法哪个先被执行?

    直接看代码 public class BlockAndMain { public static void main(String[] args) { System.out.println(" ...

  6. 代码管理工具libgit2sharp与sharpsvn

    在使用libgit2sharp 开发时出现: LibGit2Sharp.LibGit2SharpException: too many redirects or authentication repl ...

  7. shell脚本(一)

     shell脚本(一) 定义:脚本就是一条条命令的堆积.常见脚本有:js asp,jsp,php,python Shell特点:简单易用高效 Shell分类:图形界面(gui shell) 命令行界面 ...

  8. 性能测试Jmeter扩展学习-添加自定义函数

    我们在使用jmeter的时候有时候会碰到jmeter现有插件或功能也无法支持的场景,比如前端加密,此时我们就需要自己手动编写函数并导入了,下面就是手动修改并导入的过程. 首先我们需要下载jmeter源 ...

  9. 性能测试day05_Jmeter学习

    今天来学习下jmeter这个性能测试工具,虽然说性能测试最主要的是整个性能的思路,但是也少不了工具的帮忙,从以前主流的LR到jmeter的兴起,不过对于性能测试来说,个人感觉jmeter比较适合接口性 ...

  10. IntelliJ IDEA 构建maven多模块项目

    我们在开发中 因为项目之间需要依赖 所以会在maven创建多个项目配置依赖,这种项目结构主要应用在大型项目中,多人协作开发 1.创建一个项目 File ->NEW -> Projec 2. ...