IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

开源世界中的算法与数据结构 2 -- Linux Skbuff实现

kernelchina blogs 2012-02-05 23:17:33 累计浏览 4,128 次
本机暂存

兼回忆贴,大概在03年开始接触Linux的协议栈代码,那个时候还找不到什么参考书,有些东西自己搞明白了但是也从来没想过在那个论坛发个帖子什么的,也没有现在的记录总结的习惯. 后来有一本08年版的《TCP/IP Architecture, Design and Implementation in Linux》其中解析了Linux2.4 协议栈的多数代码,其中第五章专门涉及skbuff的代码实现讲解,非常详细,这个小短文不翻译整个章节,各种网文很多,只是想根据关键部分说一下我理解的设计背后的原因。

  • skbuff的形态1

image

上面是最基本的一个sk_buff了,一个TCP报文的skbuff示意图,这里隐含着一些内容:

报文分析的标准模式

skbuff采用的大块控制结构对象指向数据区是标准的实现模式,例如BSD这样的OS以及工程实现中都是这样的。

参数的集中

与其提供多个对象或者多个参数保存报文信息,不如将其集中起来存放,即便于功能扩展时保持接口稳定性,又减少了接口参数,更加容易理解和记忆。的时候仅仅是因为缩减参数长度这一简单理由,我们就搞一个XXContext的数据结构,收容各种变量,而不顾内聚耦合之类的编程标准。

skb_shared_info是个什么东东,为什么放在了数据报文的尾部?

代码中的注解是,这个部分数据是各个skbuff之间共享的。这个结构其实是控制结构,但是为什么放在数据区呢?我的理解是,因为其在多个克隆的skbuff之间共享,如果放在控制区则会造成克隆后的skbuff之中数据冗余。

  • skbuff的形态2 - 带有Paged Data的skbuff

image

上图可见除了标准数据区的报文内容外,skb_shared_info会有多个frag部分指向离散存储的Page数据。所有Page数据和标准数据区的数据的总和是报文内容。我并未遇到过这种情况,根据【1】中的描述,在使用Scatter-gather DMA技术的时候可以利用Paged Data。http://bbs.ednchina.com/BLOG_ARTICLE_73287.HTM之中描述了Scatter-gather DMA技术。因为“连续的存储器地址在物理上不一定是连续的,所以DMA传输要分成多次完成。”,采用Scatter-gather技术的优点是,通过DMA传送多块离散数据之后产生一次中断,这样减少了中断次数。

上述数据结构设计就是我所说的因为系统对于数据结构的影响而引入的对于数据结构的修改,课堂不会遇到。

  • skbuff的形态3 - IP Fragment

image

上图是fragment的组织图,同属一分片的报文会串接到第一个分片的frag_list上面。

由此可见虽然skb_shared_info之中的frag_list和frags名字差不多,容易混淆,实际二者的功能是完全不同的。

 skbuff的形态4 - 混合完全版

image

上图是【1】之中的图5.5,skbuff之中既包括了fragment也有paged data。

下面是Linux2.4之中的sk_buff的定义,供参考。

struct sk_buff {
    /* These two members must be first. */
    struct sk_buff    * next;            /* Next buffer in list                 */
    struct sk_buff    * prev;            /* Previous buffer in list           */

    struct sk_buff_head * list;        /* List we are on                     */   用于回指,便于引用next和prev所在的list。
    struct sock    *sk;                     /* Socket we are owned by             */
    struct timeval    stamp;            /* Time we arrived                */
    struct net_device    *dev;        /* Device we arrived on/are leaving by        */
    struct net_device    *real_dev;    /* For support of point to point protocols
                           (e.g. 802.3ad) over bonding, we must save the
                           physical device that got the packet before
                           replacing skb->dev with the virtual device.  */

    /* Transport layer header */  指向四层头
    union
    {
        struct tcphdr    *th;
        struct udphdr    *uh;
        struct icmphdr    *icmph;
        struct igmphdr    *igmph;
        struct iphdr    *ipiph;
        struct spxhdr    *spxh;
        unsigned char    *raw;
    } h;

    /* Network layer header */   指向三层头
    union
    {
        struct iphdr    *iph;
        struct ipv6hdr    *ipv6h;
        struct arphdr    *arph;
        struct ipxhdr    *ipxh;
        unsigned char    *raw;
    } nh;
    /* Link layer header */         指向二层头
    union
    {   
          struct ethhdr    *ethernet;
          unsigned char     *raw;
    } mac;

    struct  dst_entry *dst;

    /*
     * 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];               这一段保存中间信息,例如IP处理的时候一些Option内容保存在这里

    unsigned int     len;            /* Length of actual data            */              SKB上挂接的网络报文的整体长度
     unsigned int     data_len;                                                                     第一片之外的分片报文的数据长度
    unsigned int    csum;            /* Checksum                     */
    unsigned char     __unused,        /* Dead field, may be reused            */
            cloned,         /* head may be cloned (check refcnt to be sure). */    SKB是克隆还是原始的
              pkt_type,        /* Packet class                    */
              ip_summed;        /* Driver fed us an IP checksum            */
    __u32        priority;        /* Packet queueing priority            */
    atomic_t    users;            /* User count - see datagram.c,tcp.c         */
    unsigned short    protocol;        /* Packet protocol from driver.         */
    unsigned short    security;        /* Security level of packet            */
    unsigned int    truesize;        /* Buffer size                     */               包含skb控制块+数据块的整体内存大小

    unsigned char    *head;            /* Head of buffer                 */
    unsigned char    *data;            /* Data head pointer            */        报文解析到哪里,这个指针移动到哪里
    unsigned char    *tail;            /* Tail pointer                    */
    unsigned char     *end;            /* End pointer                    */

    void         (*destructor)(struct sk_buff *);    /* Destruct function        */
#ifdef CONFIG_NETFILTER
    /* Can be used for communication between hooks. */
        unsigned long    nfmark;
    /* Cache info */
    __u32        nfcache;
    /* Associated connection, if any */
    struct nf_ct_info *nfct;
#ifdef CONFIG_NETFILTER_DEBUG
        unsigned int nf_debug;
#endif
#endif /*CONFIG_NETFILTER*/

#if defined(CONFIG_HIPPI)
    union{
        __u32    ifield;
    } private;
#endif

#ifdef CONFIG_NET_SCHED
       __u32           tc_index;               /* traffic control index */
#endif
};

References:

【1】.《TCP/IP Architecture, Design and Implementation in Linux》- charpter 5 - “sk_buff AND PROTOCOL HEADERS”

同分类推荐文章

  1. Four Levels Of Customer Understanding (2026-05-22 21:00:00)
  2. 除法的意义 (2026-04-12 20:52:17)
  3. 第五章:共识算法 (2026-03-18 08:00:00)

查看更多 算法 文章 →

建议继续学习

  1. 红黑树并没有我们想象的那么难(上) (累计阅读 21,420)
  2. 为什么算法这么难? (累计阅读 12,334)
  3. 浅谈MySQL索引背后的数据结构及算法 (累计阅读 11,587)
  4. 加州求职记 (累计阅读 11,468)
  5. 海量数据面试题举例 (累计阅读 11,006)
  6. 基于Redis构建系统的经验和教训 (累计阅读 10,453)
  7. 谷歌(Google)2011年校园招聘笔试题 (累计阅读 9,530)
  8. 浅谈redis数据库的键值设计 (累计阅读 9,294)
  9. 关于使用STL的红黑树map还是hashmap的问题 (累计阅读 8,813)
  10. 再谈“我是怎么招聘程序员的” (累计阅读 8,735)