一、网络代码常见数据结构

struct sk_buff
 struct net_device

struct sock(本文不涉及)

二、 套接字缓冲区:sk_buff结构

涉及文件:src/net/core/dev.c、src/include/linux/skbuff.h

任何一个网络封包都会存储在这里。各个网络分层都会使用这个结构来储存其报头、有关用户数据的信息等。这个是linux网络代码中最重要的数据结构,代表已经接收或者正在传输的数据结构。结构体数据字段大致分为:

Layout(布局字段,主要是维护链表和维护skb_buff数据)
General(通用字段)
Feature-specific(功能专用字段)

Management functions(管理函数)

sk_buff结构体具体内容(linux3.0.8)如下:

struct sk_buff {
    /* These two members must be first. */
    struct sk_buff      *next;
    struct sk_buff      *prev;

ktime_t         tstamp;

struct sock     *sk;
    struct net_device   *dev;

/*
     * 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] __aligned(8);

unsigned long       _skb_refdst;
#ifdef CONFIG_XFRM
    struct  sec_path    *sp;
#endif

unsigned int        len,
                data_len;
    __u16           mac_len,
                hdr_len;
    union {
        __wsum      csum;
        struct {
            __u16   csum_start;
            __u16   csum_offset;
        };
    };
    __u32           priority;
    kmemcheck_bitfield_begin(flags1);
    __u8            local_df:1,
                cloned:1,
                ip_summed:2,
                nohdr:1,
                nfctinfo:3;
    __u8            pkt_type:3,
                fclone:2,
                ipvs_property:1,
                peeked:1,
                nf_trace:1;
    kmemcheck_bitfield_end(flags1);

__be16          protocol;

void            (*destructor)(struct sk_buff *skb);
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
    struct nf_conntrack *nfct;
#endif
#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
    struct sk_buff      *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
    struct nf_bridge_info   *nf_bridge;
#endif

int         skb_iif;
#ifdef CONFIG_NET_SCHED
    __u16           tc_index;   /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
    __u16           tc_verd;    /* traffic control verdict */
#endif
#endif

__u32           rxhash;

__u16           queue_mapping;

kmemcheck_bitfield_begin(flags2);
#ifdef CONFIG_IPV6_NDISC_NODETYPE
    __u8            ndisc_nodetype:2;
#endif
    __u8            ooo_okay:1;
    kmemcheck_bitfield_end(flags2);

/* 0/13 bit hole */

#ifdef CONFIG_NET_DMA
    dma_cookie_t        dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK
    __u32           secmark;
#endif
    union {
        __u32       mark;
        __u32       dropcount;
    };

__u16           vlan_tci;

sk_buff_data_t      transport_header;
    sk_buff_data_t      network_header;
    sk_buff_data_t      mac_header;
    /* These elements must be at the end, see alloc_skb() for details.  */
    sk_buff_data_t      tail;
    sk_buff_data_t      end;
    unsigned char       *head,
                *data;
    unsigned int        truesize;
    atomic_t        users;
};

2.1 layout字段

该字段主要是为了方便搜寻以及组织sk_buff数据结构本身。通过一个双向链表来维护sk_buff数据结构,为了迅速找出整个链表头,额外增加了一个sk_buff_head结构(src/include/linux/skbuff.h)。sk_buff与sk_buff_head的关系图如下(摘自深入理解linux网络技术):

1、sk_buff_head结构初始化

(1)网络设备驱动(以stmmac为例)

在stmmac_dvr_probe()函数里初始化一个回收sk_buff队列:skb_queue_head_init(&priv->rx_recycle),priv为网卡私有数据结构:struct stmmac_priv。

(2)网络设备处理(公共)

在net_dev_init函数里进行了两个初始化处理:skb_queue_head_init(&sd->input_pkt_queue)和skb_queue_head_init(&sd->process_queue),sd为每个CPU的网络数据结构softnet_data,因此每个CPU都有sk_buff处理的相应队列。

(3)其他一些字段

struct sock *sk:指向拥有此缓冲区的套接字的sock数据结构。当数据在本地产生或者正由本地进程接受时,就需要这个指针。

unsigned int len:指缓冲区中数据区块的大小,包括主要缓冲区(由head指)的数据以及一些片段的数据。

unsigned int data_len:只计算片段中的数据大小

__u16 mac_len:MAC报头的大小

__u16 hdr_len:head头的大小

2.2 general字段

(1)ktime_t tstamp

时间戳记,通常只对一个已经接收的封包才有意义,表示封包何时被接收,或者有时用于表示封包预定传输的时间。在接受函数netif_rx or netif_receive_skb对该字段进行设置。

(2)struct net_device *dev

此字段描述一个网络设备,其类型net_device。当接收到一个封包时,该字段填充接收该封包的设备;当发送一个封包时,该字段填发送该封包的设备。

(3)unsigned long _skb_refdst

由路由子系统使用,目的地入口

(4)char cb[48] __aligned(8)

这是一个控制缓冲区或者私有数据存储空间,保存每层的控制信息,各个层通过宏来进行访问。

(5)校验和及关联标志

union {
        __wsum      csum;
        struct {
            __u16   csum_start;
            __u16   csum_offset;
        };
    };

和ip_summed:2,

(6)位段

kmemcheck_bitfield_begin(flags1);
    __u8            local_df:1, //本地可切片标志
                cloned:1,  //表示该缓冲区为另外一个sk_buff的克隆
                ip_summed:2, //驱动程序是否进行校验表示
                nohdr:1, //
                nfctinfo:3; //netfilter使用
    __u8            pkt_type:3, //根据帧的L2目的地址进行类型划分,表示主要数据包的类型:多播,单薄,回环等
                fclone:2,
                ipvs_property:1,
                peeked:1,
                nf_trace:1;
    kmemcheck_bitfield_end(flags1);

(7)__u32 priority

表示正被传输或转发的封包QOS(服务质量)优先级

(8) __be16 protocol

驱动程序通过该字段识别L3层的协议,通知上层使用哪个处理例程,典型的有IP、IPV6、ARP等。

2.3 功能专用字段

这些字段一般只有当内核配置支持特定功能时才会被包含近sk_buff数据结构,如:netfilter防火墙 或QOS。
(1)netfilter防火墙使用
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
    struct nf_conntrack *nfct;
#endif
#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
    struct sk_buff      *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
    struct nf_bridge_info   *nf_bridge;
#endif
(2)流量控制
#ifdef CONFIG_NET_SCHED
    __u16           tc_index;   /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
    __u16           tc_verd;    /* traffic control verdict */
#endif
#endif

2.4 相关操作函数

内核通常提供许多很短、很简单的函数,用以操作sk_buff元素或元素列表。
(1)skb内存申请dev_allock_skb 和 alloc_skb
alloc_skb分配缓冲区主要函数,会进行两次内存申请,一次是从cache使用slab申请报头(即为sizeof(struct sk_buff)),第二次是使用kmalloc取得一个数据缓冲区。
skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node);
....
size = SKB_DATA_ALIGN(size);
data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info)
struct skb_shared_info:主要处理有些IP片段。
申请完之后进行初始化:
skb->truesize = size + sizeof(struct sk_buff);
    atomic_set(&skb->users, 1);
    skb->head = data;
    skb->data = data;
    skb_reset_tail_pointer(skb);
    skb->end = skb->tail + size;
dev_allock_skb由设备驱动程序使用的缓冲区分配函数,该函数只是一个包裹alloc_skb的函数,在申请大小多添加了NET_IP_ALIGN,并使用skb_reserve进行保留skb_reserve(skb, NET_IP_ALIGN) NET_IP_ALIGN=2,把IP对其在16字节地址边界上。由于Ethernet帧有14个字节头,保留两个字节后,IP报头就可以从缓冲区开始按照16字节边界对齐,并紧接在Ethernet报头之后。
(2)释放内存:kfree_skb 和 dev_kfree_skb
(3  skb_reserve
该函数会在缓冲区的头部预留一些空间,通常允许插入一个报头,或者强迫数据对齐某个边界。
static inline void skb_reserve(struct sk_buff *skb, int len)
{       
    skb->data += len;
    skb->tail += len;
}
(4)skb_put
该函数在缓冲区末尾添加数据
unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
{
    unsigned char *tmp = skb_tail_pointer(skb);
    SKB_LINEAR_ASSERT(skb);
    skb->tail += len;
    skb->len  += len;
    if (unlikely(skb->tail > skb->end))
        skb_over_panic(skb, len, __builtin_return_address(0));
    return tmp;
}
(5)skb_push
该函数在缓冲区开头添加数据
unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
{
    skb->data -= len;
    skb->len  += len;
    if (unlikely(skb->data<skb->head))
        skb_under_panic(skb, len, __builtin_return_address(0));
    return skb->data;
}
(6)skb_pull
该函数从缓冲区开端删除数据,包裹函数__skb_pull
static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len)
{
    skb->len -= len;
    BUG_ON(skb->len < skb->data_len);
    return skb->data += len;
}
(7)struct skb_shared_info
data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info)在数据缓冲区的末尾申请该数据结构,紧接在标记数据尾端的end指针之后,用于保存此数据区块的附加信息IP分片:
struct skb_shared_info {
    unsigned short  nr_frags;
    unsigned short  gso_size;
    /* Warning: this field is not always filled in (UFO)! */
    unsigned short  gso_segs;
    unsigned short  gso_type;
    __be32          ip6_frag_id;
    __u8        tx_flags;
    struct sk_buff  *frag_list;
    struct skb_shared_hwtstamps hwtstamps;
    /*
     * Warning : all fields before dataref are cleared in __alloc_skb()
     */
    atomic_t    dataref;
    /* Intermediate layers must ensure that destructor_arg
     * remains valid until skb destructor */
    void *      destructor_arg;
    /* must be last field, see pskb_expand_head() */
    skb_frag_t  frags[MAX_SKB_FRAGS];
};
sk_buff结构中没有指向该数据结构的字段,为了访问该数据结构,函数必须使用返回end指针的skb_shinfo宏进行访问:
#define skb_shinfo(SKB) ((struct skb_shared_info *)(skb_end_pointer(SKB)))
(8)缓冲区的克隆和拷贝
只是读取一个缓冲区并不修改数据区内容——克隆;另外,一种是需要修改数据区内容甚至连片段数据区内容也要修改——拷贝。克隆使用函数skb_clone,拷贝使用函数pskb_copy和skb_copy,两者的区别是后者也拷贝片段数据。
(9)列表管理函数
这些函数会操作sk_buff元素列表,具体如下:
skb_queue_head_init:初始化一个队列头
skb_queue_head, skb_queue_tail:把一个缓冲区分别添加到队列的头或尾
skb_dequeue, skb_dequeue_tail:把一个缓冲区从队列的头或尾删除
skb_queue_purge:把队列清空
skb_queue_walk:依次循环运行队列里中的每个元素 

三、 网络设kfree_skb备:net_device结构

每种网络设备都用这个数据结构表示,包括软硬件信息。

第二章 关键数据结构相关推荐

  1. 第二章 基本数据结构

    第二章 基本数据结构 第一课 模块(库)初识 分类: 标准库: 不需要安装的库 第三方库: 需要安装的库 sys模块: import sys print(sys.path) #打印环境变量(模块在这些 ...

  2. 第二章 函数式数据结构

    一. 定义一个函数式的List 通常使用trait关键字引入一种数据类型 sealed trait表示这个trait的所有实现都必须定义在这个文件里 MyList有2种实现,空和非空.非空借口由初始的 ...

  3. 第二章 关系数据结构及关系的完整性习题解析

    1.某关系R的外键是指 A.(正确答案)解析:其它关系的候选键,可以是R中的主属性或非主属性 B.(错误答案)解析:外键是另一个关系的主键 C.(错误答案)解析:其它关系的候选键,可以是R中的主属性或 ...

  4. 第二章 关键技术介绍

    本项目主要用到的技术有os.requests.lxml.pandas等,数据库使用的是SQL. 2.1   os模块介绍 Python的标准库中的os模块包含普遍的操作系统功能.即它允许一个程序在编写 ...

  5. 数据结构第二章-线性表(详细知识点总结)

    目录 第二章 线性表 2.1 线性表的定义和操作 2.1.1 线性表的定义 2.1.2 线性表的基本操作 2.2线性表的顺序表示 2.2.1 顺序表的定义 2.2.2 顺序表上基本操作的实现 2.3 ...

  6. C语言数据结构-第二章线性表-电大

    第二章线性表--内容简介 本章将进入线性结构的学习. 线性结构是最简单.最常用的一种数据结构. 本章将学习线性表的定义.顺序和链式两种存储方式及相应存储结构上的运算实现.通过典型示例训练,掌握线性表的 ...

  7. 数据结构第二章学习总结

    数据结构第二章学习总结 在数据结构第二章,我们学习了线性表的两种结构顺序和链式结构,学习了他们的定义,不同,存储结构和逻辑结构,还有一些基本的操作. 在学习过程中,我发现了许多问题:①书上的大多数代码 ...

  8. 删除第一个_学习数据结构--第二章:线性表(顺序存储、插入、删除)

    第二章:线性表(顺序表示) 1.线性表的定义和基本操作 线性表是具有相同数据类型的n(n≥0)个数据元素的有限序列 线性表中第一个元素称为表头元素;最后一个元素称为表尾元素. 除第一个元素外,每个元素 ...

  9. 为什么我要放弃javaScript数据结构与算法(第二章)—— 数组

    第二章 数组 几乎所有的编程语言都原生支持数组类型,因为数组是最简单的内存数据结构.JavaScript里也有数组类型,虽然它的第一个版本并没有支持数组.本章将深入学习数组数据结构和它的能力. 为什么 ...

  10. 【数据结构总结】第二章:线性表

    第二章:线性表 提示:本文主要是以思维导图的形式概括数据结构第一章的精华内容,基本不会用到文字性的内容,目的是为了给大家梳理每个重要的知识点的相关概念,方便大家在复盘的时候快速阅读和浏览,加快记忆速度 ...

最新文章

  1. 卷积神经网络理解及1*1卷积核的作用
  2. R语言ggplot2可视化分组散点图、使用scale_shape_manual函数、scale_color_manual函数、scale_size_manual函自定义设置分组散点的形状、大小、颜色
  3. NTU课程笔记 mas714复习:例题
  4. layui设置按钮不可点击_(eblog)7、博客发布收藏、用户中心的设置
  5. java语言中的标识符_Java语言基本语法(一)————关键字标识符(Java语言标识符命名规范Java语言的包名、类名、接口名、变量名、函数名、常量名命名规则 )...
  6. 线程调度优先级和关联性
  7. hive架构及使用场景
  8. 浅析NVR主流芯片方案
  9. 所有赚不到钱的人都是干了自己不愿意或者自己很讨厌的事
  10. 【数据结构】思维导图补充知识
  11. [转载] python3 闭包
  12. 数据挖掘 文本分类(二)搜集中文语料库与ICTCLAS分词
  13. 微信指纹支付提示java6_苹果6微信指纹支付每次提示请验证已有的指纹用于支付,怎样才能支付?...
  14. 40 张最全计算机网络基础思维导图
  15. PhpSpreadsheet读取excel
  16. 关于求最大公倍数的不同算法比较
  17. es linux下使用api进行es故障操作处理
  18. 2022-12-12 系统移植
  19. fastdfs上传文件资料(PDF,视频,图片,FileCaseUtil,FileUploadUtil)并生成缩略图
  20. 【企业信息化系列】如何以BPM流程平台为核心串联公司业务系统

热门文章

  1. Linux学习笔记(8)文件搜索与帮助(find)
  2. 【bzoj3456】城市规划(多项式求逆+dp)
  3. hammer的初始化及移动端各种滑动
  4. 杨森翔的书法-10斗方:杜牧中秋月
  5. 2013年阿里巴巴实习生笔试题
  6. FOREACH的遍历
  7. 重启Oracle 服务
  8. sendEmail invalid SSL_version at SSL.pm
  9. whose view is not in the window hierarchy
  10. vim忽略大写和小写查找配置