1.netdev_pbuf_alloc函数简介
netdev_pbuf_alloc是网卡驱动中的内存申请函数,申请一块缓冲区用于存放网络报文数据。
2.pbuf结构分析
netdev_pbuf_alloc函数以pbuf结构申请内存空间,如程序清单 2.1所示。
程序清单 2.1  pbuf结构

struct pbuf {
    struct pbuf      next;                                                                 
    void            
payload;
    u16_t            tot_len;
    u16_t            len;
    u8_t             type;
    u8_t             flags;
    u16_t            ref;
};​

3.pbuf_alloc函数封装
netdev_pbuf_alloc函数是pbuf_alloc函数的函数封装,对外提供的函数接口只带有一个参数用于指定申请的缓冲区大小。而pbuf_alloc函数有三个参数,分别是申请的pbuf数据缓冲区是否带有偏移,数据缓冲区大小及缓冲区类型。在netdev_pbuf_alloc函数中,申请的pbuf结构缓冲区数据偏移固定为0,缓冲区类型为PBUF_POOL。netdev_pbuf_alloc函数传入的第二个参数缓冲区大小在调用pbuf_alloc函数申请pbuf结构时会被增加一个reserve长度,用于协议栈中的部分特殊操作。
3.1 pbuf_alloc函数第一个参数分析
在网络中通信的数据报文,除了真正的数据段外,还包括了TCP/IP各层协议的报文头。如用户有特殊需求,要在应用层构建报文并直接通过网卡驱动发送,则需要申请pbuf结构的数据缓冲区带有各层偏移,如程序清单 3.1所示。
程序清单 3.1  pbuf_alloc第一个参数分析
typedef enum {
  PBUF_TRANSPORT,                                        /  传输层报文头偏移        /
  PBUF_IP,                                               /  IP层报文头偏移         /
  PBUF_LINK,                                             /  链路层报文头偏移        /
  PBUF_RAW_TX,                                           /  封装链路层偏移         /
  PBUF_RAW                                               /  无偏移                 /
} pbuf_layer;

3.2 pbuf_alloc函数第三个参数分析
申请pbuf结构需要指定pbuf类型,原因是不同类型的pbuf申请内存的方式不同,如程序清单 3.2所示。
程序清单 3.2  pbuf_alloc第三个参数分析
typedef enum {
  PBUF_RAM,                                                 /  内存堆分配             /
  PBUF_ROM,                                                 /  指向ROM空间内数据    /
  PBUF_REF,                                                 /  指向RAM空间内数据    /
  PBUF_POOL                                                 /  内存池分配             /
} pbuf_type;

PBUF_RAM类型的pbuf主要通过内存堆分配得到,协议栈划分了一块空间用于申请PBUF_RAM类型的pbuf,划分的空间大小通过“libsylixos/SylixOS/config/net/net_perf_cfg.h”中的“LW_CFG_LWIP_MEM_SIZE”宏配置。
PBUF_REF和PBUF_ROM类型的pbuf基本相同,前者指向ROM空间内的某段数据,而后者指向RAM空间内的某段数据。PBUF_REF和PBUF_ROM类型的pbuf通过“libsylixos/SylixOS/config/net/net_perf_cfg.h”中的“LW_CFG_LWIP_NUM_PBUFS”宏配置在内存池中预分配pbuf的最大数量。
PBUF_POOL类型的pbuf通过内存池分配,这种类型的pbuf可以在极短时间内得到分配,在网卡驱动接收数据时,一般采用这种方式。协议栈会在内存池中预先分配适当数量和大小的内存空间,通过“libsylixos/SylixOS/config/net/net_perf_cfg.h”中的“LW_CFG_LWIP_POOL_SIZE和LW_CFG_LWIP_NUM_POOLS”宏配置。
3.3 PBUF_POOL类型pbuf申请流程
netdev_pbuf_alloc函数申请PBUF_POOL类型pbuf,若申请的缓冲区大于PBUF_POOL池中单个pbuf缓冲区长度,系统会分配多个固定大小的PBUF_POOL类型pbuf,并把这些pbuf链成一个链表,以满足用户的分配空间请求,如图 3.1所示。

图 3.1  PBUF_POOL类型pbuf申请流程
4.实际应用
网卡驱动收到数据后,会调用netdev_pbuf_alloc函数申请pbuf结构,并将数据拷贝至pbuf。当有大量数据被网卡驱动接收时,可能会造成申请pbuf结构失败,原因是系统预分配的内存池中的pbuf结构已经全部被申请,还未被释放。可以尝试通过“libsylixos/SylixOS/config/net/net_perf_cfg.h”中的“LW_CFG_LWIP_POOL_SIZE和LW_CFG_LWIP_NUM_POOLS”宏配置增大pbuf缓冲区大小和数量解决pbuf结构申请失败问题。