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从它所在的链表上取下。
    以上的链表操作都是先关中断的。这在中断上下文中是不需要的,所以另外有一套与上面函数同名但是有前缀“__”的函数供运行在中断上下文中的函数调用。


  • 相关阅读:
    使用FormData,进行Ajax请求并上传文件
    oracle视图详解
    Unicode和UTF-8之间的关系
    如何访问tomcat所在服务器的其他盘符的资源。
    React首次渲染过程
    react知识点总结
    WebSocket基础
    gulp 批量添加类名 在一个任务中使用多个文件来源
    Chrome浏览器取消INPUT自动记忆下拉框
    Angular7 Drag and Drop
  • 原文地址:https://www.cnblogs.com/yi-mu-xi/p/10762404.html
Copyright © 2011-2022 走看看