网络套接口缓存SKB

195 阅读2分钟
TCP/IP网络结构分为物理层,数据链路层,网络层,传输层,应用层。每一层都有各自独有的数据报文结构,而且数据长度不固定,并且会频繁在头部尾部插入数据包头,校验码等数据信息,从计算机资源效率角度来看,应该尽量避免数据的复制——综上可知,网络协议对系统的存储还是有很高的要求。
在LINUX网络子系统中套接口缓存(SOCKET BUFFER)对应的数据结构为sk_buff,简称为SKB。成员大致分为以下几类:
  1. 与SKB组织相关的成员变量
  2. 通用成员变量
  3. 标志性变量
  4. 与特征相关的成员变量
在缓冲区头部查入数据意味着改变修改指向缓冲区的指针,内核提供skb_reserve()来完成此功能,做法是会现在缓冲区头部预留部分空间,在上层向下层传递SKB会使用这部分的空间。

struct sk_buff {    /* These two members must be first. */    struct sk_buff      *next;    struct sk_buff      *prev;    struct sock     *sk;    struct skb_timeval  tstamp;    struct net_device   *dev;    struct net_device   *input_dev;    union {        struct tcphdr   *th;        struct udphdr   *uh;        struct icmphdr  *icmph;        struct igmphdr  *igmph;        struct iphdr    *ipiph;        struct ipv6hdr  *ipv6h;        unsigned char   *raw;    } h;    union {        struct iphdr    *iph;        struct ipv6hdr  *ipv6h;        struct arphdr   *arph;        unsigned char   *raw;    } nh;    union {        unsigned char   *raw;    } mac;    struct  dst_entry   *dst;    struct  sec_path    *sp;    /*     * This is the control buffer. It is free to use for every     * layer. Please put your private variables there. If you     * want to keep them across layers you have to do a skb_clone()     * first. This is owned by whoever has the skb queued ATM.     */    char            cb[48];    unsigned int        len,                data_len,                mac_len;    union {        __wsum      csum;        __u32       csum_offset;    };    __u32           priority;    __u8            local_df:1,                cloned:1,                ip_summed:2,                nohdr:1,                nfctinfo:3;    __u8            pkt_type:3,                fclone:2,                ipvs_property:1;    __be16          protocol;    void            (*destructor)(struct sk_buff *skb);#ifdef CONFIG_NETFILTER    struct nf_conntrack *nfct;#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)    struct sk_buff      *nfct_reasm;#endif#ifdef CONFIG_BRIDGE_NETFILTER    struct nf_bridge_info   *nf_bridge;#endif#endif /* CONFIG_NETFILTER */#ifdef CONFIG_NET_SCHED    __u16           tc_index;   /* traffic control index */#ifdef CONFIG_NET_CLS_ACT    __u16           tc_verd;    /* traffic control verdict */#endif#endif#ifdef CONFIG_NET_DMA    dma_cookie_t        dma_cookie;#endif#ifdef CONFIG_NETWORK_SECMARK    __u32           secmark;#endif    __u32           mark;    /* These elements must be at the end, see alloc_skb() for details.  */    unsigned int        truesize;    atomic_t        users;    unsigned char       *head,                *data,                *tail,                *end;};

以上是sk_buffer的结构,可以看到有许多#ifdef预编译指令,表示只有在编译时定义宏后,特定的成员变量才会起作用,SKB有两部分——SKB结构体,数据缓冲区(head)。

unsigned char *head, *data, *tail, *end;

四个指针用来指向线性数据缓冲区及数据部分边界。head和end指向缓存区头与尾,data和tail指向数据区头与尾,每一层协议会在head和tail之间插入协议首部。这片空间便是预留给头部协议的。也可以在tail和end之间添加数据。

那么SKB如何相互组织在一起——通过双向链表。

struct sk_buff *next; struct sk_buff *prev;

并且会在sk_buff之前加一个sk_buff_head的结构头节点

struct sk_buff_head {    /* These two members must be first. */    struct sk_buff  *next;    struct sk_buff  *prev;    __u32       qlen;    spinlock_t  lock;};

__u32 qlen是32位无符号整形的数值表示队列长度,也就是SKB个数,lock是自旋锁。

未完待续........