zoukankan      html  css  js  c++  java
  • [转]linux网络协议栈(1)——socket buffer

    [转自 https://www.cnblogs.com/hustcat/archive/2009/09/19/1569859.html]

    Linux网络核心数据结构是套接字缓存(socket buffer),简称skb。它代表一个要发送或处理的报文,并贯穿于整个协议栈。

    1、    套接字缓存

    skb由两部分组成:
    (1)    报文数据:它保存了实际在网络中传输的数据;
    (2)    管理数据:供内核处理报文的额外数据,这些数据构成了协议之间交换的控制信息。
    当应用程序向一个socket传输数据之后,该socket将创建相应的套接字缓存,并将用户数据拷贝到缓存中。当报文在各协议层传达输的过程中,每一导的报文头将插入到用户数据之前。skb为报文头申请了足够的空间,所以避免了由于插入报文头而对报文进行多次拷贝。用户数据只拷贝了两次:一是从用户空间拷贝到内核;二是报文数据从内核传送到网络适配器。

    1.1、sk_buff

    套接字缓存结构:

      1 //套接字缓存
      2 struct sk_buff {
      3     /* These two members must be first. */
      4     struct sk_buff        *next;  
      5     struct sk_buff        *prev;
      6 
      7     struct sk_buff_head    *list;  
      8     struct sock        *sk;          //指向创建报文的socket
      9     struct timeval        stamp;  //此报文收到时的时间
     10     struct net_device    *dev;          //收到此报文的网络设备 
     11     struct net_device    *input_dev;
     12     struct net_device    *real_dev;
     13 
     14     //TCP报头
     15     union {  
     16         struct tcphdr    *th;   //tcp头
     17         struct udphdr    *uh;  //udp头
     18         struct icmphdr    *icmph;
     19         struct igmphdr    *igmph;
     20         struct iphdr    *ipiph;
     21         struct ipv6hdr    *ipv6h;
     22         unsigned char    *raw;
     23     } h;
     24     //IP报头
     25     union {
     26         struct iphdr    *iph;
     27         struct ipv6hdr    *ipv6h;
     28         struct arphdr    *arph;
     29         unsigned char    *raw;
     30     } nh;
     31     //链路层帧头
     32     union {
     33           unsigned char     *raw;
     34     } mac;
     35 
     36     struct  dst_entry    *dst;  //此报文的路由,路由确定后赋此值
     37     struct    sec_path    *sp;
     38 
     39     /*
     40      * This is the control buffer. It is free to use for every
     41      * layer. Please put your private variables there. If you
     42      * want to keep them across layers you have to do a skb_clone()
     43      * first. This is owned by whoever has the skb queued ATM.
     44      */
     45     char            cb[40];
     46     
     47     //此报文的长度,这是指网络报文在不同协议层中的长度,包括头部和数据。在协议栈的不同层,这个长度是不同的。 
     48     unsigned int        len,
     49                 data_len,
     50                 mac_len,
     51                 csum;
     52     unsigned char        local_df,
     53                 cloned,
     54                 pkt_type, //网络报文的类型,常见的有PACKET_HOST,代表发给本机的报文;还有PACKET_OUTGOING,代表本机发出的报文。 
     55                 ip_summed;
     56     __u32            priority;
     57      
     58     unsigned short        protocol,//链路层协议
     59                 security;
     60 
     61     void            (*destructor)(struct sk_buff *skb);
     62 #ifdef CONFIG_NETFILTER
     63         unsigned long        nfmark;
     64     __u32            nfcache;
     65     __u32            nfctinfo;
     66     struct nf_conntrack    *nfct;
     67 #ifdef CONFIG_NETFILTER_DEBUG
     68         unsigned int        nf_debug;
     69 #endif
     70 #ifdef CONFIG_BRIDGE_NETFILTER
     71     struct nf_bridge_info    *nf_bridge;
     72 #endif
     73 #endif /* CONFIG_NETFILTER */
     74 #if defined(CONFIG_HIPPI)
     75     union {
     76         __u32        ifield;
     77     } private;
     78 #endif
     79 #ifdef CONFIG_NET_SCHED
     80        __u32            tc_index;        /* traffic control index */
     81 #ifdef CONFIG_NET_CLS_ACT
     82     __u32           tc_verd;               /* traffic control verdict */
     83     __u32           tc_classid;            /* traffic control classid */
     84 #endif
     85 
     86 #endif
     87 
     88 
     89     /* These elements must be at the end, see alloc_skb() for details.  */
     90 
     91        //此报文存储区的长度,这个长度是16字节对齐的,一般要比报文的长度大
     92     unsigned int        truesize;
     93     atomic_t        users;
     94     /*head和end指向报文数据的整个单元.head与data之间的空间称为headroom,tail与end之间的空间称为tailroom.
     95     */
     96     unsigned char        *head,
     97                 *data,
     98                 *tail,
     99                 *end;
    100 };
    View Code

    1.2、与sk_buff相关的函数

    与sk_buff相关的函数涉及到网络报文存储结构和控制结构的分配、复制、释放,以及控制结构里的各指针的操作,还有各种标志的检查。重要的函数说明如下:

    struct sk_buff *alloc_skb(unsigned int size,int gfp_mask)
    分配大小为size的存储空间存放网络报文,同时分配它的控制结构。size的值是16字节对齐的,gfp_mask是内存分配的优先级。常见的内存分配优先级有GFP_ATOMIC,代表分配过程不能被中断,一般用于中断上下文中分配内存;GFP_KERNEL,代表分配过程可以被中断,相应的分配请求被放到等待队列中。分配成功之后,因为还没有存放具体的网络报文,所以sk_buff的 data,tail指针都指向存储空间的起始地址,len的大小为0,而且 is_clone和cloned两个标记的值都是0。

    struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask)
    从控制结构skb中 clone出一个新的控制结构,它们都指向同一个网络报文。clone成功之后,将新的控制结构和原来的控制结构的 is_clone,cloned两个标记都置位。同时还增加网络报文的引用计数(这个引用计数存放在存储空间的结束地址的内存中,由函数atomic_t *skb_datarefp(struct sk_buff *skb)访问,引用计数记录了这个存储空间有多少个控制结构)。由于存在多个控制结构指向同一个存储空间的情况,所以在修改存储空间里面的内容时,先要确定这个存储空间的引用计数为1,或者用下面的拷贝函数复制一个新的存储空间,然后才可以修改它里面的内容。

    struct sk_buff *skb_copy(struct sk_buff *skb, int gfp_mask)
    复制控制结构skb和它所指的存储空间的内容。复制成功之后,新的控制结构和存储空间与原来的控制结构和存储空间相对独立。所以新的控制结构里的is_clone,cloned两个标记都是0,而且新的存储空间的引用计数是1。
     
    void kfree_skb(struct sk_buff *skb)
    释放控制结构skb和它所指的存储空间。由于一个存储空间可以有多个控制结构,所以只有在存储空间的引用计数为1的情况下才释放存储空间,一般情况下,只释放控制结构skb。

    unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
    将tail指针下移,并增加skb的 len值。data和 tail之间的空间就是可以存放网络报文的空间。这个操作增加了可以存储网络报文的空间,但是增加不能使tail的值大于end的值,skb的 len值大于truesize的值。

    unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
    将data指针上移,并增加skb的 len值。这个操作在存储空间的头部增加了一段可以存储网络报文的空间,上一个操作在存储空间的尾部增加了一段可以存储网络报文的空间。但是增加不能使data的值小于head的值,skb的 len值大于truesize的值。

    unsigned char * skb_pull(struct sk_buff *skb, unsigned int len)
    将data指针下移,并减小skb的 len值。这个操作使data指针指向下一层网络报文的头部。

    void skb_reserve(struct sk_buff *skb, unsigned int len)
    将data指针和tail指针同时下移。这个操作在存储空间的头部预留 len长度的空隙。
     
    void skb_trim(struct sk_buff *skb, unsigned int len)
    将网络报文的长度缩减到 len。这个操作丢弃了网络报文尾部的填充值。

    int skb_cloned(struct sk_buff *skb)
    判断skb是否是一个 clone的控制结构。如果是clone的,它的cloned标记是1,而且它指向的存储空间的引用计数大于1。

    2、    套接字缓存队列(Socket-Buffer Queues)

    2.1、sk_buff_head

    在网络协议栈的实现中,有时需要把许多网络报文放到一个队列中做异步处
    理。LINUX 为此定义了相关的数据结构 sk_buff_head。这是一个双向链表的
    头,它把sk_buff链接成一个双向链表。

    1 //套接字缓存队列头
    2 struct sk_buff_head {
    3     /* These two members must be first. */
    4     struct sk_buff    *next;
    5     struct sk_buff    *prev;
    6 
    7     __u32        qlen; //队列的长度,即队列中报文的数量
    8     spinlock_t    lock;
    9 };

    2.2、与 sk_buff_head相关的函数

    void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
    将newsk加到链表 list的头部。

    void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
    将newsk加到链表 list的尾部。

    struct sk_buff *skb_dequeue(struct sk_buff_head *list)
    从链表 list的头部取下一个 sk_buff。

    struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list)
    从链表 list的尾部取下一个 sk_buff。
     
    skb_insert(struct sk_buff *old, struct sk_buff *newsk)
    将newsk加到old所在的链表上,并且 newsk在old的前面。
     
    void skb_append(struct sk_buff *old, struct sk_buff *newsk)
    将newsk加到old所在的链表上,并且 newsk在old的后面。
     
    void skb_unlink(struct sk_buff *skb)
    将skb从它所在的链表上取下。
    以上的链表操作都是先关中断的。这在中断上下文中是不需要的,所以另外有一套与上面函数同名但是有前缀“__”的函数供运行在中断上下文中的函数调用。


  • 相关阅读:
    二进制位运算
    Leetcode 373. Find K Pairs with Smallest Sums
    priority_queue的用法
    Leetcode 110. Balanced Binary Tree
    Leetcode 104. Maximum Depth of Binary Tree
    Leetcode 111. Minimum Depth of Binary Tree
    Leetcode 64. Minimum Path Sum
    Leetcode 63. Unique Paths II
    经典的递归练习
    案例:java中的基本排序
  • 原文地址:https://www.cnblogs.com/yi-mu-xi/p/10762404.html
Copyright © 2011-2022 走看看