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;
      }
      //初始化pbuf
      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;
    }

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为最后一个pbuf
      u16_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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/956233.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙动态路由实现方案

背景 随着CSDN 鸿蒙APP 业务功能的增加&#xff0c;以及为了与iOS、Android 端统一页面跳转路由&#xff0c;以及动态下发路由链接&#xff0c;路由重定向等功能。鸿蒙动态路由方案的实现迫在眉睫。 实现方案 鸿蒙版本动态路由的实现原理&#xff0c;类似于 iOS与Android的实…

登录认证(1):登录的基本逻辑及实现思路

登录 在当今的大部分网站、应用、游戏中&#xff0c;为了确保资源的安全性和隐私保护&#xff0c;通常需要用户先进行身份验证&#xff08;即登录&#xff09;&#xff0c;然后才能使用特定的功能和服务。这样的做法不仅增加了系统安全性&#xff0c;还能够根据用户的偏好提供…

音乐播放器实现:前端HTML,CSS,JavaScript综合大项目

音乐播放器实现:前端HTML&#xff0c;CSS&#xff0c;JavaScript综合大项目 项目概述项目视图效果一、侧边栏相关代码&#xff08;一&#xff09;HTML代码&#xff08;二&#xff09;css代码 二、登录页面&#xff08;一&#xff09;HTML代码&#xff08;二&#xff09;css代码…

【js进阶】设计模式之单例模式的几种声明方式

单例模式&#xff0c;简言之就是一个类无论实例化多少次&#xff0c;最终都是同一个对象 原生js的几个辅助方式的实现 手写forEch,map,filter Array.prototype.MyForEach function (callback) {for (let i 0; i < this.length; i) {callback(this[i], i, this);} };con…

git系列之revert回滚

1. Git 使用cherry-pick“摘樱桃” step 1&#xff1a; 本地切到远程分支&#xff0c;对齐要对齐的base分支&#xff0c;举例子 localmap git pull git reset --hard localmap 对应的commit idstep 2&#xff1a; 执行cherry-pick命令 git cherry-pick abc123这样就会将远程…

【Web】2025西湖论剑·中国杭州网络安全安全技能大赛题解(全)

目录 Rank-l Rank-U sqli or not Rank-l username存在报错回显&#xff0c;发现可以打SSTI 本地起一个服务&#xff0c;折半查找fuzz黑名单&#xff0c;不断扔给fenjing去迭代改payload from flask import Flask, request, render_template_stringapp Flask(__name__)app…

Android系统开发(十四):跨进程通讯的隐形之手AIDL

引言 你是否曾在 Android 开发中为进程间通讯&#xff08;IPC&#xff09;头疼不已&#xff1f;如果是&#xff0c;那么 AIDL 就是你的救星&#xff01;它不仅让跨进程数据传输变得高效&#xff0c;而且还解决了异构环境的兼容性问题。本篇文章将带你深入了解 AIDL&#xff0c…

string类的常用接口及模拟实现

目录 基础知识 常用接口 1>常见构造 2>容量操作 3>访问及遍历操作 1.迭代器 2.反向迭代器 3.范围for 4.auto 4>修改操作 5>非成员函数 其它接口 模拟实现 string.h string.cpp swap() 基础知识 string是一个管理字符的类&#xff0c;定义在std命…

Linux——多线程的控制

Linux——线程的慨念及控制-CSDN博客 文章目录 目录 文章目录 前言 一、线程函数的认识 1、基本函数的回顾 1、线程的创建pthread_create 2、线程阻塞pthread_join 3、线程退出pthread_exit 2、线程的分离pthread_detach 3、互斥锁初始化函数&#xff1a;pthread_mutex_init 4、…

计算机网络 (49)网络安全问题概述

前言 计算机网络安全问题是一个复杂且多维的领域&#xff0c;它涉及到网络系统的硬件、软件以及数据的安全保护&#xff0c;确保这些元素不因偶然的或恶意的原因而遭到破坏、更改或泄露。 一、计算机网络安全的定义 计算机网络安全是指利用网络管理控制和技术措施&#xff0c;保…

TCP状态转移图详解

状态 描述 LISTEN represents waiting for a connection request from any remote TCP and port. SYN-SENT represents waiting for a matching connection request after having sent a connection request. SYN-RECEIVED represents waiting for a confirming connect…

VUE学习笔记(入门)5__vue指令v-html

v-html是用来解析字符串标签 示例 <!doctype html> <html lang"en"> <head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document<…

【论文投稿】探秘计算机视觉算法:开启智能视觉新时代

目录 引言 一、计算机视觉算法基石&#xff1a;图像基础与预处理 二、特征提取&#xff1a;视觉信息的精华萃取 三、目标检测&#xff1a;从图像中精准定位目标 四、图像分类&#xff1a;识别图像所属类别 五、语义分割&#xff1a;理解图像的像素级语义 六、计算机视觉…

【Java数据结构】Java对象的比较

元素的比较 基本类型比较 在Java中基本类型比较可以直接比较大小 &#xff0c;返回一个布尔类型&#xff08;true或者false&#xff09;。 int a 10; int b 20; System.out.println(a>b); System.out.println(ab); System.out.println(a<b);对象比较的问题 对象的比…

《自动驾驶与机器人中的SLAM技术》ch8:基于预积分和图优化的紧耦合 LIO 系统

和组合导航一样&#xff0c;也可以通过预积分 IMU 因子加上雷达残差来实现基于预积分和图优化的紧耦合 LIO 系统。一些现代的 Lidar SLAM 系统也采用了这种方式。相比滤波器方法来说&#xff0c;预积分因子可以更方便地整合到现有的优化框架中&#xff0c;从开发到实现都更为便…

Ubuntu 24.04 LTS 更改软件源

Ubuntu 24.04 LTS 修改软件源

【2024年度技术总结】Unity 游戏开发的深度探索与实践

文章目录 前言一、Unity 游戏开发的技术深度总结1、C# 编程基础2、Unity 基础入门3、Unity 实战技巧4、Unity 小技巧分享 二、技术工具与平台的年度使用心得1、学习资源的选择2、开发环境配置3、测试与调试工具 三、技术项目实战经验与成果展示1、【制作100个Unity游戏】专栏2、…

ingress-nginx代理tcp使其能外部访问mysql

一、helm部署mysql主从复制 helm repo add bitnami https://charts.bitnami.com/bitnami helm repo updatehelm pull bitnami/mysql 解压后编辑values.yaml文件&#xff0c;修改如下&#xff08;storageclass已设置默认类&#xff09; 117 ## param architecture MySQL archit…

Top期刊算法!RIME-CNN-BiLSTM-Attention系列四模型多变量时序预测

Top期刊算法&#xff01;RIME-CNN-BiLSTM-Attention系列四模型多变量时序预测 目录 Top期刊算法&#xff01;RIME-CNN-BiLSTM-Attention系列四模型多变量时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于RIME-CNN-BiLSTM-Attention、CNN-BiLSTM-Attention、R…

游戏引擎学习第84天

仓库:https://gitee.com/mrxiao_com/2d_game_2 我们正在试图弄清楚如何完成我们的世界构建 上周做了一些偏离计划的工作&#xff0c;开发了一个小型的背景位图合成工具&#xff0c;这个工具做得还不错&#xff0c;虽然是临时拼凑的&#xff0c;但验证了背景构建的思路。这个过…