LwIP协议栈的数据包管理

      首先,谈谈数据包管理的重要性。我们知道TCP/IP协议机构中,每一层都被描述成独立(各个层被封装)的模块,每一层负责完成自己独立的事情。这样在性能优良的处理器上跑TCP/IP协议是没有问题的,一断我们的处理器很LOW的时候,特别是嵌入式开发的时候,这样做就不行。因为每一层都独立后,每一层之间的通信涉及到繁琐的数据的复制传递,这既消耗内存,也耗费时间。

      LwIP协议栈是TCP/IP协议的一种轻量级实现。它主要是为了避免当处理器性能不好的时候,数据的传递耗费大量的内存和时间。故它并没有采用完整的分层结构,它会假设各层之间的部分数据结构和实现原理在其他层是可见的。这样数据在传递过程中就不需要频繁的复制和传送了,各个层的字段会直接操作这些数据(因为是可见的)。省内存、省时间。这些可见的数据就需要一种有效的管理机制,这就是数据包管理的由来,由此也可以看出有效的管理数据包是十分重要的。这涉及到LWIP协议栈设计的核心思想。

       同时,LwIP协议栈采用了这样一种进程模型:

     (1)协议栈内核和操作系统相互隔离,同时整个协议栈作为操作系统的一个进程存在。这样用户应用程序和协议栈之间的通信是通过回调函数实现的,即:Raw/Callback API.

     (2)用户应用程序可以单独作为一个进程存在

         即:Sequential API    和   Socket API

在LwIP协议源码中,与数据包管理的文件是  pbuf.c和pbuf.h  ,在pbuf.h中,定义了一个pbuf结构体:

  struct pbuf {
   /** next pbuf in singly linked pbuf chain */  
            struct pbuf *next;                          //构成pbuf链表时指向下一个pbuf结构
  /** pointer to the actual data in the buffer */
            void *payload;                              //数据指针,指向pbuf所记录的数据区域
  /**
   * 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;                             //整个pbuf链表的长度
  /** length of this buffer */
             u16_t len;                                   //当前pbuf链表的长度
 /** pbuf_type as u8_t instead of enum to save space */
             u8_t /*pbuf_type*/ type;            //pbuf类型
 /** misc flags */
             u8_t flags;                                //未使用,一般为0
/**
   * 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有4中类型

typedef enum {
  PBUF_RAM,    /* pbuf data is stored in RAM */
  PBUF_ROM,   /* pbuf data is stored in ROM */
  PBUF_REF,     /* pbuf comes from the pbuf pool */
  PBUF_POOL  /* pbuf payload refers to RAM */

} pbuf_type;

对于 PBUF_RAM类型的pbuf,其内存分配函数为:

p=(struct pbuf *)mem_malloc(LWIP_ALIGN_SIZE(SIZEOF_STRUCT_PBUF+offset)+LWIP_ALIGN_SIZE(size));

注意,这里用的是内存堆分配函数。

   
                                                                               图1. PBUF_RAM型pbuf


对于 PBUF_POOL类型的pbuf,其内存分配函数为:

P=memp_malloc(MEMP_PBUF_POOL);      //注意这里用的是内存池分配函数


                                                                                     图2.  PBUF_POOL型pbuf

     下面看看源码是怎么申请PBUF_ROM,   PBUF_REF类型的,其中p是pbuf指针

  p=memp_malloc(MEMP_PBUF);     //注意这里用的是内存池分配函数,分配的pool刚好存储pbuf结构体,内存空间在其他地                                                           //方,ROM或者是RAM区  ,不像前面两个是在连续的存储空间  

图3.  PBUF_ROM/REF型pbuf

数据包的形式多种多样,常见的就是以上几种类型组合起来的pbuf链表

                                                               图4. 不同类型pbuf组成的pbuf链表

下面来看看数据包的一些管理函数:

(1)数据包申请函数

      struct pbuf *pbuf_alloc(pbuf_layer l, u16_t length, pbuf_type type);   

     参数l指明pbuf所处的层次,pbuf_layer 类型在pbuf.h的文件中的定义为:
     typedef enum {
                 PBUF_TRANSPORT,  //传输层
                 PBUF_IP,                   //网络层
                 PBUF_LINK,              //链路层
                 PBUF_RAW              //原始层,offset的值为0

} pbuf_layer;

参数length表示要申请的pbuf的数据区长度,参数type支出需要申请的pbuf类型

(2)数据包释放函数
         直接给出源码:
u8_t  pbuf_free(struct pbuf *p)
{
  u16_t type;
  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;

  }

其他数据包管理函数为:
void pbuf_realloc(struct pbuf *p, u16_t size); 
u8_t pbuf_header(struct pbuf *p, s16_t header_size);
void pbuf_ref(struct pbuf *p);
u8_t pbuf_free(struct pbuf *p);
u8_t pbuf_clen(struct pbuf *p);  
void pbuf_cat(struct pbuf *head, struct pbuf *tail);
void pbuf_chain(struct pbuf *head, struct pbuf *tail);
struct pbuf *pbuf_dechain(struct pbuf *p);
err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from);
u16_t pbuf_copy_partial(struct pbuf *p, void *dataptr, u16_t len, u16_t offset);
err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len);
struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer);
#if LWIP_CHECKSUM_ON_COPY
err_t pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
                       u16_t len, u16_t *chksum);
#endif /* LWIP_CHECKSUM_ON_COPY */
u8_t pbuf_get_at(struct pbuf* p, u16_t offset);
u16_t pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n);
u16_t pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset);
u16_t pbuf_strstr(struct pbuf* p, const char* substr);


  


猜你喜欢

转载自blog.csdn.net/u014069939/article/details/80390132
今日推荐