自学内容网 自学内容网

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;
}

原文地址:https://blog.csdn.net/Fresh_man111/article/details/145239800

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!