struct sk_buff结构体详解

`sk_buff` 结构体详解(包含全生命周期解析) Linux网络数据包核心结构sk_buff解析与生命周期管理 sk_buff是Linux网络协议栈处理数据包的核心数据结构,包含数据缓冲区、协议信息和设备信息等关键成员。其生命周期分为接收和发送两条路径:接收路径从网卡驱动创建sk_buff开始,经协议栈处理后交付应用层;发送路径则由应用层发起,经协议栈封装后由网卡驱动发送。文中详细解析了sk_buff的内存布局、关键操作函数(如分配/释放、数据操作、分片管理等)以及特殊场景处理(如零拷贝)。最后提供了内存预分配、DMA优化等性能优化技巧。 阅读详情

struct sk_buff结构体详解

 
 

    struct sk_buff是linux网络系统中的核心结构体,linux网络中的所有数据包的封装以及解封装都是在这个结构体的基础上进行。

<span style="color:#333333"><span style="color:black"><code class="language-cpp"><span style="color:#0077aa">struct</span> sk_buff_head 
<span style="color:#999999">{</span>
    <span style="color:#0077aa">struct</span> sk_buff <span style="color:#9a6e3a">*</span>next<span style="color:#999999">;</span>
    <span style="color:#0077aa">struct</span> sk_buff <span style="color:#9a6e3a">*</span>prev<span style="color:#999999">;</span>
    
    __u32 qlen<span style="color:#999999">;</span>
    spinlock_t lock<span style="color:#999999">;</span>
<span style="color:#999999">}</span>


<span style="color:#0077aa">struct</span> sk_buff
<span style="color:#999999">{</span>
    <span style="color:#0077aa">struct</span> sk_buff <span style="color:#9a6e3a">*</span>next<span style="color:#999999">;</span>
    <span style="color:#0077aa">struct</span> sk_buff <span style="color:#9a6e3a">*</span>prev<span style="color:#999999">;</span>
    <span style="color:#0077aa">struct</span> sock <span style="color:#9a6e3a">*</span>sock <span style="color:#999999">;</span><span style="color:slategray">//struct sock是socket在网络层的表示,其中存放了网络层的信息</span>
    
    <span style="color:#0077aa">unsigned</span> <span style="color:#0077aa">int</span> len<span style="color:#999999">;</span><span style="color:slategray">//下面有介绍</span>
    <span style="color:#0077aa">unsigned</span> <span style="color:#0077aa">int</span> data_len<span style="color:#999999">;</span> <span style="color:slategray">//下面有介绍</span>
    __u16   mac_len <span style="color:#999999">;</span> <span style="color:slategray">//数路链路层的头长度</span>
    __u16   hdr_len <span style="color:#999999">;</span> <span style="color:slategray">//writable header length of cloned skb</span>
    <span style="color:#0077aa">unsigned</span> <span style="color:#0077aa">int</span> truesize <span style="color:#999999">;</span> <span style="color:slategray">//socket buffer(套接字缓存区的大小)</span>
    atomic_t users <span style="color:#999999">;</span> <span style="color:slategray">//对当前的struct sk_buff结构体的引用次数;</span>
    __u32   priority <span style="color:#999999">;</span> <span style="color:slategray">//这个struct sk_buff结构体的优先级</span>
    
    sk_buff_data_t transport_header <span style="color:#999999">;</span> <span style="color:slategray">//传输层头部的偏移量</span>
    sk_buff_data_t network_header <span style="color:#999999">;</span>   <span style="color:slategray">//网络层头部的偏移量</span>
    sk_buff_data_t mac_header <span style="color:#999999">;</span>       <span style="color:slategray">//数据链路层头部的偏移量</span>
    
    <span style="color:#0077aa">char</span> <span style="color:#9a6e3a">*</span>data <span style="color:#999999">;</span> <span style="color:slategray">//socket buffer中数据的起始位置;</span>
    sk_buff_data_t tail <span style="color:#999999">;</span> <span style="color:slategray">//socket buffer中数据的结束位置;</span>
    <span style="color:#0077aa">char</span> <span style="color:#9a6e3a">*</span>head <span style="color:#999999">;</span> <span style="color:slategray">//socket buffer缓存区的起始位置;</span>
    sk_buffer_data_t end <span style="color:#999999">;</span> <span style="color:slategray">//socket buffer缓存区的终止位置;</span>
    
    <span style="color:#0077aa">struct</span> net_device <span style="color:#9a6e3a">*</span>dev<span style="color:#999999">;</span> <span style="color:slategray">//将要发送struct sk_buff结构体的网络设备或struct sk_buff的接收</span>
                            <span style="color:slategray">//网络设备</span>
    <span style="color:#0077aa">int</span> iif<span style="color:#999999">;</span>  <span style="color:slategray">//网络设备的接口索引号;</span>
    
    
    <span style="color:#0077aa">struct</span> timeval tstamp <span style="color:#999999">;</span> <span style="color:slategray">//用于存放接受的数据包的到达时间;</span>
    
    __u8  local_df <span style="color:#9a6e3a">:</span> <span style="color:#990055">1</span> <span style="color:#999999">,</span>  <span style="color:slategray">//allow local fragmentaion;</span>
          cloned   <span style="color:#9a6e3a">:</span> <span style="color:#990055">1</span> <span style="color:#999999">,</span> <span style="color:slategray">// head may be cloned</span>
          <span style="color:#999999">;</span>
    
    __u8  pkt_type <span style="color:#9a6e3a">:</span> <span style="color:#990055">3</span> <span style="color:#999999">,</span> <span style="color:slategray">//数据包的类型;</span>
          fclone   <span style="color:#9a6e3a">:</span> <span style="color:#990055">2</span><span style="color:#999999">,</span>  <span style="color:slategray">// struct sk_buff clone status</span>
    
<span style="color:#999999">}</span></code></span></span>
 

 

   下图展示了一个很大的数据块,linux内核是如何利用不同的struct sk_buff来把它组织起来的:

   而struct sk_buff中的len字段的含义为:

   len = l(1) + l(2) + l(3) + l(n+1);

   data_len =  l(2) + l(3) + l(n+1);

   所以线性数据的长度 l(1) = skb->len - skb->data_len;

    data_len 中存放的是非线性的数据,也就是整体上不是连续的数据;

    l(1)  表示的是线性的数据,是连续的。

wKiom1SBTrmjU_9mAAGxQnga-r8480.jpg

    下图解释了struct sk_buff中head, end, data, tail字段的含义:

wKioL1R-_8KTjAVtAAFAABRaZmU892.jpg

 

struct sk_buff结构体中的pkt_type字段的取值为:


wKiom1R_GsqQHNxAAAOTvV_MN8I617.jpg

 

   通过struct sk_buff中的pkt_type字段中的值,可以判断出接收到的数据包是不是发送给本机的数据包。

   如果pkt_type == PACKET_HOST,说明收到的数据包是发送给本机的单播数据包

   如果pkt_type == PACKET_BROADCAST,说明收到的数据包是发送给本机的广播数据包

   如果pkt_type == PACKET_MULTICAST,说明收到的数据包是发送给本机的组播数据包

   如果pkt_type == PACKET_OTHERHOST,说明收到的数据包不是发送给本机的数据包,需要转发出去。

 

 

struct sk_buff中的len字段的含义:

wKioL1R_HWyjx6cZAACQEnuD1qQ184.jpg

 

 

   1.分配一个struct sk_buff结构体(socket buffer套接字缓存区):

   static inline struct sk_buff *alloc_skb( unsigned int size,

                 gfp_t priority)

   size : 为将要分配的缓存区的大小;

   priority : 取值为GFP_ATOMIC, GFP_KERNEL等;

 

<span style="color:#333333"><span style="color:black"><code class="language-cpp"><span style="color:#0077aa">static</span> <span style="color:#0077aa">inline</span> <span style="color:#0077aa">struct</span> sk_buff <span style="color:#9a6e3a">*</span><span style="color:#dd4a68">alloc_skb</span><span style="color:#999999">(</span><span style="color:#0077aa">unsigned</span> <span style="color:#0077aa">int</span> size<span style="color:#999999">,</span> gft_t priority<span style="color:#999999">)</span>
<span style="color:#999999">{</span>
    <span style="color:#0077aa">return</span> <span style="color:#dd4a68">__alloc_skb</span><span style="color:#999999">(</span>size<span style="color:#999999">,</span> priority<span style="color:#999999">,</span> <span style="color:#990055">0</span><span style="color:#999999">,</span> <span style="color:#9a6e3a">-</span><span style="color:#990055">1</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span></code></span></span>
 

    struct sk_buff *__alloc_skb(unsigned int size, gft_t priority,

                      int fclone, int node);

    EXPORT_SYMBOL(__alloc_skb);

    size : 为将要分配的缓存区的大小;

    priority : 同上;

    fclone   : 取1时,表示的是对当前的struct sk_buff进行克隆;

               取0时,表示不对当前struct sk_buff进行克隆;

   

<span style="color:#333333"><span style="color:black"><code class="language-cpp"><span style="color:#0077aa">struct</span> sk_buff <span style="color:#9a6e3a">*</span><span style="color:#dd4a68">__alloc_skb</span><span style="color:#999999">(</span><span style="color:#0077aa">unsigned</span> <span style="color:#0077aa">int</span> size<span style="color:#999999">,</span> gft_t priority<span style="color:#999999">,</span> <span style="color:#0077aa">int</span> fclone<span style="color:#999999">,</span> <span style="color:#0077aa">int</span> node<span style="color:#999999">)</span>
<span style="color:#999999">{</span>
   <span style="color:#0077aa">struct</span> sk_buff <span style="color:#9a6e3a">*</span>skb<span style="color:#999999">;</span>
   <span style="color:#0077aa">struct</span> skb_shared_info <span style="color:#9a6e3a">*</span>shinfo<span style="color:#999999">;</span>
   u8 <span style="color:#9a6e3a">*</span>data<span style="color:#999999">;</span>
   
   <span style="color:slategray">//分配struct sk_buff结构体;</span>
   skb <span style="color:#9a6e3a">=</span> <span style="color:#dd4a68">kmem_cache_alloc_node</span><span style="color:#999999">(</span>cache<span style="color:#999999">,</span> gft_mask <span style="color:#9a6e3a">&</span> <span style="color:#9a6e3a">~</span>__GFP_DMA<span style="color:#999999">,</span> node<span style="color:#999999">)</span><span style="color:#999999">;</span>
   
   <span style="color:slategray">//对size的大小进行边界对其处理;</span>
   size <span style="color:#9a6e3a">=</span> <span style="color:#dd4a68">SKB_DATA_ALIGN</span><span style="color:#999999">(</span>size<span style="color:#999999">)</span><span style="color:#999999">;</span>
   
   <span style="color:slategray">//分配size大小的缓存区以及struct skb_shared_info结构体;</span>
   data <span style="color:#9a6e3a">=</span> <span style="color:#dd4a68">kmalloc_node_track_caller</span><span style="color:#999999">(</span> size <span style="color:#9a6e3a">+</span> <span style="color:#0077aa">sizeof</span><span style="color:#999999">(</span><span style="color:#0077aa">struct</span> skb_shared_info<span style="color:#999999">)</span><span style="color:#999999">,</span> gft_mask<span style="color:#999999">,</span> node<span style="color:#999999">)</span><span style="color:#999999">;</span>
   
   skb<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>truesize <span style="color:#9a6e3a">=</span> size <span style="color:#9a6e3a">+</span> <span style="color:#0077aa">sizeof</span><span style="color:#999999">(</span><span style="color:#0077aa">struct</span> sk_buff<span style="color:#999999">)</span><span style="color:#999999">;</span>
   <span style="color:#dd4a68">atomic_set</span><span style="color:#999999">(</span> <span style="color:#9a6e3a">&</span>skb<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>users<span style="color:#999999">,</span> <span style="color:#990055">1</span><span style="color:#999999">)</span><span style="color:#999999">;</span> <span style="color:slategray">//引用用户数设置为1</span>
   
   skb<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>head <span style="color:#9a6e3a">=</span> data<span style="color:#999999">;</span>  <span style="color:slategray">// head 指向缓存区的起始位置</span>
   skb<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>data <span style="color:#9a6e3a">=</span> data<span style="color:#999999">;</span>  <span style="color:slategray">// data 指向缓存区的起始位置</span>
   <span style="color:#dd4a68">skb_reset_tail_pointer</span><span style="color:#999999">(</span>skb<span style="color:#999999">)</span><span style="color:#999999">;</span> <span style="color:slategray">// 等价于 skb->tail = data</span>
   skb<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>end <span style="color:#9a6e3a">=</span> skb<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>tail <span style="color:#9a6e3a">+</span> size<span style="color:#999999">;</span> <span style="color:slategray">// end 指向缓存区的终止位置</span>
   
   <span style="color:slategray">//对struct skb_shared_info中的字段进行初始化处理;</span>
   shinfo <span style="color:#9a6e3a">=</span> <span style="color:#dd4a68">skb_shinfo</span><span style="color:#999999">(</span>skb<span style="color:#999999">)</span><span style="color:#999999">;</span> <span style="color:slategray">// #define skb_shinfo(skb) (struct skb_shared_info *)(skb->end)</span>
   <span style="color:#dd4a68">atomic_set</span><span style="color:#999999">(</span><span style="color:#9a6e3a">&</span>shinfo<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>dataref<span style="color:#999999">,</span> <span style="color:#990055">1</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
   shinfo<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>nr_frags <span style="color:#9a6e3a">=</span> <span style="color:#990055">0</span><span style="color:#999999">;</span>
   shinfo<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>gso_size <span style="color:#9a6e3a">=</span> <span style="color:#990055">0</span><span style="color:#999999">;</span>
   shinfo<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>gso_segs <span style="color:#9a6e3a">=</span> <span style="color:#990055">0</span><span style="color:#999999">;</span>
   shinfo<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>gso_type <span style="color:#9a6e3a">=</span> <span style="color:#990055">0</span><span style="color:#999999">;</span>
   shinfo<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>ipv6_frag_id <span style="color:#9a6e3a">=</span> <span style="color:#990055">0</span><span style="color:#999999">;</span>
   shinfo<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>tx_flags<span style="color:#999999">.</span>flags <span style="color:#9a6e3a">=</span> <span style="color:#990055">0</span><span style="color:#999999">;</span>
   <span style="color:#dd4a68">skb_frag_list_init</span><span style="color:#999999">(</span>skb<span style="color:#999999">)</span><span style="color:#999999">;</span> <span style="color:slategray">// 等价于 skb_shinfo(skb)->frag_list = NULL</span>
   
   <span style="color:#0077aa">return</span> skb<span style="color:#999999">;</span>
<span style="color:#999999">}</span></code></span></span>
 

 

wKioL1R_MnKwmTiDAAGCKAMkv_0553.jpg

 

  

    2.释放一个struct sk_buff结构体:

      void kfree_skb(struct sk_buff *skb);

      EXPORT_SYMBOL(kfree_skb);

      当在使用kfree_skb()函数释放一个struct sk_buff结构体时,先会判断

skb->users的值是否为1。

     如果skb->users > 1,则只进行atomic_dec(&skb->user)操作,然后返回

     如果skb->users = 1,则才会将struct sk_buff结构体所占的内存还给系统。

 

      3.向sk_buff套接字缓存区的数据区的尾部加入len长的数据。

       unsigend char *skb_put(struct sk_buff *skb, u32 len);

       EXPORT_SYMBOL(skb_put);

wKioL1SAJe3RINouAALOf8UzJIU980.jpg

 

      skb_put()函数执行之前的sk_buff中的data,tail字段的位置:

wKioL1SAJm6SDDkbAACDtwaRY5M123.jpg

         skb_put(struct sk_buff *skb, unsigned int n)执行之后的效果:

wKioL1SAJsrQaTUxAACOIJxXqDM629.jpg

      可以通过使用skb_put()函数,来向sk_buff套接字缓存区的尾部去添加数据;

 

    4.向sk_buff套接字缓存区的数据区的头部加入len个字节的数据:

      unsigned char *skb_push(struct sk_buff *skb, u32 len);

      EXPORT_SYMBOL(skb_push);

      wKiom1SAJ9zxJ_SUAAOgBlZv8j8018.jpg

 

     skb_push()之前的sk_buff中的data,tail字段的位置:

wKiom1SAKE2h9AosAACNyHYoHq0296.jpg

      skb_push(struct sk_buff *skb, unsigned int len)之后的效果图:

wKiom1SAKMbTKcMWAACGtVImCHg523.jpg

 

     5.删除sk_buff缓存区中的数据区头部的len个字节的数据:

      unsigned char *skb_pull(struct sk_buff *skb, u32 len);

      EXPORT_SYMBOL(skb_pull);

wKiom1SAKnaiinwGAAFK_CGXVy0660.jpg

 

wKioL1SAKxfwx71aAAMo06t7jZo341.jpg

 

      skb_pull()执行之前的sk_buff中的data, tail指针位置:

wKiom1SAKuzhZmuTAACIyeC1O3U422.jpg

      skb_pull(struct sk_buff *skb, unsigned int len)执行之后的效果图:

wKiom1SAKzSR0nxwAACTH7MNyMs251.jpg

 

  

 

    skb_put(struct sk_buff *skb, u32 len)向套接字缓存区中添加数据的方式跟队列中添加数据的方式是一样样的。每次添加数据向尾部添加。

    skb_push(struct sk_buff *skb, u32 len)像向套接字缓存区中的数据区的头部添加数据,跟堆栈中添加数据的方式一样。

   skb_pull(struct sk_buff *skb, u32 len)从套接字缓存区的数据区中删除

len字节的数据。

 

   6.从套接字缓存区的数据区头部删除len个字节的数据,然后并对其中的csum字段值进行更新;

     unsigend char *skb_pull_rcsum(struct sk_buff *skb, u32 len);

     EXPORT_SYMBOL(skb_pull_rcsum);

 

wKiom1SAVq7hwIgDAAMdMxaOr7E773.jpg

 

wKioL1SAV1ODakdbAAPx1zVCN1E131.jpg

   

 

 

    7.修改套接字缓存区中的数据区的大小(通过调整tail的位置来改变数据区的大小):

     void skb_trim(struct sk_buff *skb, u32 len);

     EXPORT_SYMBOL(skb_trim);

     通过skb_trim()函数之后,struct sk_buff中的len字段的大小为:len;

 

wKiom1SAS0yCyBG6AAGKXqS-L4A781.jpg

 

wKioL1SATBSS0N11AAD6iuGXhO8893.jpg

 

 

    第二种:修改套接字缓存区中的数据区的大小(推荐使用):

   static inline int pskb_trim(struct sk_buff *skb, u32 len);

  

wKioL1SAXd2jYsHcAAHyx2wr68I277.jpg  

   wKioL1SAXkSSx-w1AAJcpLdy8co638.jpg

 

    pskb_trim()与skb_trim()的区别是:

   skb_trim()要求:struct sk_buff中的data_len 必须为0;

   pskb_trim()并没有次要求,在它的内部已经考虑了data_len = 0 和 data_len != 0,所以推荐使用pskb_trim()来调整套接字缓存区中数据的大小。

 

 

     8.将套接字缓存区中的数据区中的len个字节复制到一个buffer之中去:

      int skb_copy_bits(struct sk_buff *skb, int offset,

                         void *buffer, int len)

      EXPORT_SYMBOL(skb_copy_bits);

      返回值:成功返回0

              失败返回-EFAULT;

      wKioL1SAYg3TgDqWAAJbWacZPl8508.jpg

 

 

     9.将buffer中的len字节个数据复制到套接字缓存区中的数据区之中去:

      int skb_store_bits(struct sk_buff *skb, int offset,

                          const void *from, int len)

        EXPORT_SYMBOL(skb_store_bits);

       返回值:成功返回0;

               失败返回-EFAULT;

wKiom1SAZOyjP4XxAAOYvLevwBs266.jpg

 

 

     10.对套接字缓存去中的数据区中的len个字节的数据进行checksum ;

     u32 skb_checksum(struct sk_buff *skb, int offset, int len,

                       u32 csum);

     EXPORT_SYMBOL(skb_checksum);

    

wKiom1SAaH3Q5rwjAANPJtdvoJE843.jpg

 

    

    u32 skb_copy_and_chechsum_bits( struct sk_buff *skb, int offset,

                    void *buffer, int len, u32 csum );

   EXPORT_SYMBOL(skb_copy_and_checksum_bits);

   skb_copy_and_checksum_bits()将skb套接字缓存区中的数据区从offset偏移量开始的len个字节的数据复制到buffer之中,并对offset -- offset+len之间的数据进行checksum。

 

 

   11.对struct sk_buff结构体的克隆(即在分配一个新的struct sk_buff结构体,但是这个新的struct sk_buff结构体与之前的那个struct sk_buff结构体共用skb->data所指的数据区)

    struct sk_buff *skb_clone(struct sk_buff *skb, gft_t gfp_mask);

    EXPORT_SYMBOL(skb_clone);

wKiom1SBIVPChOT4AALbx1FKWzM960.jpg

<span style="color:#333333"><span style="color:black"><code class="language-cpp"><span style="color:#0077aa">struct</span> sk_buff <span style="color:#9a6e3a">*</span><span style="color:#dd4a68">__skb_clone</span><span style="color:#999999">(</span><span style="color:#0077aa">struct</span> sk_buff <span style="color:#9a6e3a">*</span>n<span style="color:#999999">,</span> <span style="color:#0077aa">struct</span> sk_buff <span style="color:#9a6e3a">*</span>skb<span style="color:#999999">)</span>
<span style="color:#999999">{</span>
    n<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>next <span style="color:#9a6e3a">=</span> n<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>prev <span style="color:#9a6e3a">=</span> <span style="color:#990055">NULL</span><span style="color:#999999">;</span>
    n<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>sk <span style="color:#9a6e3a">=</span> <span style="color:#990055">NULL</span><span style="color:#999999">;</span>
    <span style="color:#dd4a68">__copy_skb_header</span><span style="color:#999999">(</span>n<span style="color:#999999">,</span>skb<span style="color:#999999">)</span><span style="color:#999999">;</span> <span style="color:slategray">// 将skb中的所有相关的信息复制到n之中</span>
    
    n<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>cloned <span style="color:#9a6e3a">=</span> <span style="color:#990055">1</span><span style="color:#999999">;</span> <span style="color:slategray">//表示,这个n结构体是克隆结构体</span>
    <span style="color:#dd4a68">atomic_set</span><span style="color:#999999">(</span><span style="color:#9a6e3a">&</span>n<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>users<span style="color:#999999">,</span> <span style="color:#990055">1</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
    
    <span style="color:#dd4a68">atomic_inc</span><span style="color:#999999">(</span><span style="color:#9a6e3a">&</span><span style="color:#dd4a68">skb_shinfo</span><span style="color:#999999">(</span>skb<span style="color:#999999">)</span><span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>dataref<span style="color:#999999">)</span><span style="color:#999999">;</span> <span style="color:slategray">// 又有一个struct sk_buff结构体引用了这个数据区</span>
    
    skb<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>cloned <span style="color:#9a6e3a">=</span> <span style="color:#990055">1</span><span style="color:#999999">;</span>
    <span style="color:#0077aa">return</span> n<span style="color:#999999">;</span>
<span style="color:#999999">}</span>

<span style="color:#0077aa">struct</span> sk_buff <span style="color:#9a6e3a">*</span><span style="color:#dd4a68">skb_clone</span><span style="color:#999999">(</span><span style="color:#0077aa">struct</span> sk_buff <span style="color:#9a6e3a">*</span>skb<span style="color:#999999">,</span> gft_t gfp_mask<span style="color:#999999">)</span>
<span style="color:#999999">{</span>
    n <span style="color:#9a6e3a">=</span> <span style="color:#dd4a68">kmem_cache_alloc</span><span style="color:#999999">(</span>skbuff_head_cache<span style="color:#999999">,</span> gfp_mask<span style="color:#999999">)</span><span style="color:#999999">;</span>
    <span style="color:#0077aa">return</span> <span style="color:#dd4a68">__skb_clone</span><span style="color:#999999">(</span>n<span style="color:#999999">,</span> skb<span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span></code></span></span>
 

 

 

    12.创建一个struct sk_buff new的结构体和缓存区,让后将struct sk_buff old以及其缓存区中的内容全部复制到struct sk_buff new之后去。

      struct sk_buff *skb_copy(struct sk_buff *skb, gfp_t gfp_mask)

      EXPORT_SYMBOL(skb_copy);

wKioL1SBKD-DAO13AAQ2lJWpF-k538.jpg

<span style="color:#333333"><span style="color:black"><code class="language-cpp"><span style="color:#0077aa">struct</span> sk_buff <span style="color:#9a6e3a">*</span><span style="color:#dd4a68">skb_copy</span><span style="color:#999999">(</span><span style="color:#0077aa">struct</span> sk_buff <span style="color:#9a6e3a">*</span>skb<span style="color:#999999">,</span> gfp_t gfp_mask<span style="color:#999999">)</span>
<span style="color:#999999">{</span>
     <span style="color:#0077aa">struct</span> sk_buff <span style="color:#9a6e3a">*</span>n<span style="color:#999999">;</span>
     
     <span style="color:slategray">//分配了一个struct sk_buff结构体和(skb->end - skb->head + skb->data_len)个字节的内存</span>
     n <span style="color:#9a6e3a">=</span> <span style="color:#dd4a68">alloc_skb</span><span style="color:#999999">(</span>skb<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>end <span style="color:#9a6e3a">-</span> skb<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>head <span style="color:#9a6e3a">+</span> skb<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>data_len<span style="color:#999999">,</span> gfp_mask<span style="color:#999999">)</span> ;
     
     
     <span style="color:slategray">//skb->head开始,复制headerlen + skb->len 个字节的数据到 n->head处</span>
     <span style="color:#dd4a68">skb_copy_bits</span><span style="color:#999999">(</span> skb<span style="color:#999999">,</span> <span style="color:#9a6e3a">-</span>headerlen<span style="color:#999999">,</span> n<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>head<span style="color:#999999">,</span> headerlen <span style="color:#9a6e3a">+</span> skb<span style="color:#9a6e3a">-</span><span style="color:#9a6e3a">></span>len <span style="color:#999999">)</span><span style="color:#999999">;</span>
     
     <span style="color:slategray">//将struct sk_buff *skb中的数据复制到struct sk_buff *n 之中去</span>
     <span style="color:#dd4a68">copy_skb_header</span><span style="color:#999999">(</span>n<span style="color:#999999">,</span> skb<span style="color:#999999">)</span><span style="color:#999999">;</span>
     
     <span style="color:#0077aa">return</span> n<span style="color:#999999">;</span>
<span style="color:#999999">}</span></code></span></span>
 


   执行完skb_copy()后的结果图:

wKioL1SBUQ7AczQtAAFUv5EMkYI879.jpg

 

 

  13.struct sk_buff *pskb_copy(struct sk_buff *skb, gfp_t gfp_mask)

      EXPORT_SYMBOL(pskb_buff);

    执行完pskb_copy()之后的结果图:

wKiom1SBURnBGTJdAAERfW6J5wU786.jpg

 

 

wKioL1SAbqyjbxxmAAKjnTWwgpw964.jpg

 

     由于linux内核中对struct sk_buff的组织:是以双链表的形式组织。所以,下面的函数是对双链表的操作:

  

    0.对一个struct sk_buff_head头结构体进行初始化:

  static inline void skb_queue_head_init(struct sk_buff_head *list)

  {

     spin_lock_init(&list->lock);

 

    //等价于:list->next = list->prev = NULL;

     __skb_queue_head_init(list);

  } 

    1.删除sk_buff双链表中的第一个元素:

      struct sk_buff *skb_dequeue(struct skb_buff_head *list)

      EXPORT_SYMBOL(skb_dequeue);

      list : 为struct sk_buff的链表头;

      须知:如果list链表为空,则返回NULL;

 

    2.删除sk_buff双链表中的最末尾的元素:

      struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list)

      EXPORT_SYMBOL(skb_dequeue_tail);

      list : 为struct sk_buff的链表头;

 

    3.每次往sk_buff链表的头部后面添加一个struct sk_buff:

      void skb_queue_head(struct sk_buff_head *list,

                          struct sk_buff *new)

      EXPORT_SYMBOL(skb_queue_head);

 

    4.每次往sk_buff链表的尾部添加一个struct sk_buff:

      void skb_queue_tail(struct sk_buff_head *list,

                           struct sk_buff *new);

      EXPORT_SYMBOL(skb_queue_tail);

 

    5.从sk_buff链表中删除一个struct sk_buff:

      void skb_unlink(struct sk_buff *skb,

                       struct sk_buff_head *list)

      EXPORT_SYMBOL(skb_unlink);

 

    6.在list链表中的old后面添加一个new结构体:

      void skb_append(struct sk_buff *old, struct sk_buff *new,

                       struct sk_buff_head *list);

      EXPORT_SYMBOL(skb_append);

 

    7.在list链表中的old的前面添加一个new结构体:

      void skb_insert(struct sk_buff *old, struct sk_buff *new,

                       struct sk_buff_head *list)

      EXPORT_SYMBOL(skb_insert);

 

wKioL1SAdRmxKn_7AAHi2z5H7RM987.jpg

 

      因为struct sk_buff 的双链表组织结构,在内核中是属于临界资源,所以在每次访问这个双链表是,都要使用spin_lock_irqsave(&list->lock,flags)来获取锁,同时在操作完成以后通过spin_unlock_irqstore(&list->lock,flags)来释放锁。

    以上的提供的几个函数在其内部已经进行了对临界资源的并发访问的处理,所以可以任意使用。

 

 

    unsigned char *skb_transport_header(struct sk_buff *skb);

      用于获取skb中的TCP层协议头的位置;

wKioL1SBkezDBM5PAADSOhgW4Qo710.jpg

 

wKiom1SBkXGxNL9CAADZiVVt9h0070.jpg

     unsigned char *skb_network_header(struct sk_buff *skb);

       用于获取skb中的IP层协议头的位置;

    unsigned char *skb_mac_header(struct sk_buff *skb);

        用于获取skb中的链路层协议头的位置;

 

上面的关于skb_transport_header()的代码的实现,非常好的说明了在要获取响应的协议头的位置时,使用skb_***_header()这样的函数。因为在有的体系结构中char *transport_header类型,而在有的体系结构中unsigned int transport_header类型。而使用skb_***_header则不需要考虑它的类型。

 

    对struct sk_buff中的transport_header, network_header, mac_header等进行重新设置:

 static inline void skb_reset_transport_header(struct sk_buff *skb)

     {

          skb->transport_header = skb->data;

     }

    

static inline void skb_reset_network_header(struct sk_buff *skb)

     {

         skb->network_header = skb->data;

     }

 

 static inline void skb_reset_mac_header(struct sk_buff *skb)

     {

          skb->mac_header = skb->data;

     }

 

   

    对struct sk_buff中的transport_header, network_header, mac_header等进行offset偏移量的设置:

    static inline void skb_set_transport_header(struct sk_buff *skb, int offset)

    {

         skb->transport_header = skb->data + offset;

    }

   

    static inline void skb_set_network_header(struct sk_buff *skb, int offset)

    {

         skb->network_header = skb->data + offset;

    }

 

    static inline void skb_set_mac_header(struct sk_buff *skb, int offset)

    {

         skb->mac_header = skb->data + offset;

    }

 

 

    下面的这几个宏由于对内核中的struct sk_buff结构体组成的双链表进行各种遍历操作:

wKiom1SBmAvAxkUQAAQefpvNv8g866.jpg

 

    用于对一个struct sk_buff中的其他struct sk_buff结构体进行遍历:

wKioL1SBmR2TzOnAAAClCbeCoHE551.jpg

 

 

     如果想使用上面的函数,上面所有的函数在linux/skbuff.h中都有定义与声明,当然skbuff.h,skbuff.c,文件中还定义了其他一些函数,可以自己去看

sk_buff数据结构详解 sk_buff 简介 封包存储于struct sk_buff中,所有网络分层都会使用这个结构来储存其报头、有关用户数据的信息,以及协调其工作的其他内部信息。从第二层到第四层都会使用这个数据结构。L4附加一个报头传给L3,L3附加自己的报头传给L2,L2附加自己的报头传给L1转发出去。设备收到sk_buff之后,每层都只处理自己的报头,并将其呈交给上层。 sk_buff数据结构 内核使用一个双向链表来维护sk_buff,链表的开端使用了一个哑元元素sk_buff_head,如下图所示为sk_buff链表 阅读详情

相关推荐

深度:一文看懂内核中struct sk_buff结构分析

当网卡驱动程序收到一个UDP数据报后,它创建一个结构体struct sk_buff,确保sk_buff->data成员指向的空间足够存放收到的数据(对于数据报分片的情况,因为比较复杂,我们暂时忽略,我们假设一次收到的是一个完整的UDP数据报)。把收到的数据全部拷贝到sk_buff->data指向的空间,然后,把skb->mac.raw指向data,此时,数据报的开始位置是一个以太网头,所以skb->mac.raw指向链路层的以太网头。例如,h是一个包含内核所能解析的L4协议的数据结构的联合。

m0_74282605的博客 516

sk_buff结构分析

前言: 以下是根据《深入理解Linux网络技术内幕》对sk_buff的相关总结,由于是刚刚看这本书(太厚了),不免在前期出现错误,随着对此书的深入我会在修改前面的错误,也希望各位牛人给予指点。帮助我成长。 sk_buff分析: sk_buff是Linux网络代码中最重要的结构体之一。它是Linux在其协议栈里传送的结构体,也就是所谓的“包”,在他里面包含了各层协议的头部,比如et

jasenwan88的专栏 1008

[linux]sk_buff结构体详解

linux内核sk_buff结构体详解,从sk_buff结构到流程详细讲解sk_buff结构体

Linux内核网络驱动框架

超详细!

qq_28576837的博客 701

保姆级教程:在树莓派5B上用AI Kit跑YOLOv8,实测134fps的完整配置流程

本文提供了一份详细的树莓派5B搭配AI Kit部署YOLOv8的教程,从硬件组装到模型转换、优化和部署,最终实现134FPS的高帧率推理性能。通过Hailo-8L AI加速模块和HEF模型转换,这套方案为边缘AI计算提供了强大支持,适用于智能监控、无人机视觉导航等应用场景。

weixin_28725407的博客 448

o型圈沟槽设计_O型密封圈——详细选型过程及步骤

一、密封圈常用材料二、密封圈常用密封形式密封圈分为活塞密封,活塞杆密封,端面密封,旋转密封。三、O型密封圈的选型规则O型密封圈的尺寸图径向安装O型圈选型(静态和动态运动)活塞杆密封选择适当规格的O形圈时,应该保证其外径(d1+2*d2)至少等于或者大于沟槽外径d6;当选择的O形圈直径d1大于250 mm时, O形圈外径将不能超过沟槽外径的3%;当选择的O形圈直径d1小于250mm时, O形圈外径将...

weixin_39856630的博客 2万+

非常详细的详谈struct sk_buff

非常详细的详谈struct sk_buff 排版太慢 难看的话可以下载WOR文档 专门详解struct_sk_buff 链接: http://pan.baidu.com/s/1gf8VNKR 密码: pquc 注:本文大部分内容来源于网络收集加上自己理解,如有不托之处,请指出,本人无任何版权! 全称socket buffers,简称skb,中文名字叫套接字缓存。它作为网络数据包

zhuhuibeishadiao 1万+

电磁场仿真软件:CST Studio Suite_(25).案例分析与实践

在电磁场仿真中,材料属性的设置至关重要。通过API,您可以轻松地设置和修改材料属性,以适应不同的仿真需求。通过以上案例分析和实践,您已经学会了如何在CST Studio Suite中进行二次开发。自动化仿真流程:批量创建和运行仿真项目、优化设计参数、生成报告。自定义仿真设置:设置材料属性、边界条件和激励源。数据处理与分析:读取仿真结果、数据可视化和数据导出。高级脚本开发:多线程仿真、集成机器学习和云仿真。这些技能将帮助您更高效地进行电磁场仿真,提高设计质量和工作效率。

kkchenjj的博客 1387

struct sk_buff详解

sk_buff是Linux网络代码中最重要的结构体之一。它是Linux在其协议栈里传送的结构体,也就是所谓的“包”,在他里面包含了各层协议的头部,比如ethernet, ip ,tcp ,udp等等。也有相关的操作等。熟悉他是进一步了解Linux网络协议栈的基础。       此结构定义在头文件中,结构体布局大致可分为以下四部分: l       布局(layout) l  

执迷不悔 6258

SPICE框架:自博弈提升大语言模型推理能力

自博弈训练是提升大语言模型推理能力的前沿技术,其核心思想是通过模型自我对话实现持续迭代优化。SPICE框架创新性地采用文档驱动的自博弈机制,结合思维链提示和迭代式上下文扩展,显著提升模型在数学证明、逻辑推理等复杂任务中的表现。该技术在代码生成、金融分析等工程场景中展现出23%-41%的性能提升,其关键技术包括多样性控制、质量过滤和记忆管理。随着与RLAIF等强化学习方法的结合,这种训练范式正在推动大模型向更接近人类思维方式的推理能力进化。

weixin_28686771的博客 402

【RH850/U2A】:功能安全

功能安全相关

3130

struct sk_buff与struct socket及struct sock 结构体分析

sk_buff是Linux网络协议栈最重要的数据结构之一,该数据结构贯穿于整个数据包处理的流程。由于协议采用分层结构,上层向下层传递数据时需要增加包头,下层向上层数据时又需要去掉包头。sk_buff中保存了L2,L3,L4层的头指针,这样在层传递时只需要对数据缓冲区改变头部信息,并调整sk_buff中的指针,而不需要拷贝数据,这样大大减少了内存拷贝的需要。 /** * struct sk

wangpengqi的专栏 5489

[SC]SystemC在CPU/GPU验证中的应用(六)

本文提供了一个SystemC学习路线图,包含50个由浅入深的编程实例,涵盖从基础模块到复杂SoC建模的全过程。示例包括:基础模块(时钟、计数器)、通道(FIFO、握手)、TLM2.0接口、总线模型(AXI-Lite)、CPU/GPU内核实现、功耗分析、断言验证等。重点展示了SystemC在硬件验证中的关键应用:VCD波形生成、UVM-SystemC框架、Verilog DPI协同仿真、Python集成、NoC路由器和完整SoC建模(CPU+Cache+DRAM+互连)。每个示例提供完整代码和详细注释,建议分

元直的博客 1492

内核中struct sk_buff结构分析

struct sk_buff可能是linux网络代码中最重要的数据结构,它表示接收或发送数据包的包头信息,并包含很多成员变量供网络代码中的各子系统使用。 这个结构被网络的不同层(MAC或者其他二层链路协议,三层的IP,四层的TCP或UDP等)使用,并且其中的成员变量在结构从一层向另一层传递时改变。 L4向L3传递前会添加一个L4的头部,同样,L3向L2传递前,会添加一个L3的头部。添加头部比在不同

Macross的专栏 2505

Neo4j:入门基础(二)之导入CSV文件

导入CSV文件的方式  主要的两种方式如下:   1、使用 Cypher 命令LOAD CSV,这种方式可以处理中小型数据(可达10 million条记录)   2、使用批量导入工具neo4j-admin ,用来直接导入大数据集 三、上述方式具体导入过程   1、Cypher 命令LOAD CSV 参数 1、USING PERIODIC COMMIT 300 使用批量提交,每满300条提交一次,防止内存溢出 2、WITH HEADERS 从文件中读取第一行作为参数名,...

Puzzle的博客 3万+

DeepSeek+即梦:AI视频创作从0到1全突破

本文为 DeepSeek 与即梦生成视频全攻略,从前期注册登录、熟悉工具界面及硬件网络准备入手,助读者开启 AI 视频创作。详细阐述利用 DeepSeek 构思脚本、即梦实现画面生成与静态图转动态视频的方法,包括提示词编写、参数调整等技巧。同时介绍后期合成音画、添加字幕特效及多平台适配要点,结合实战案例解析全流程。还分享进阶提示词优化、版权注意等避坑指南,为创作者提供从 0 到 1 的 AI 视频创作完整解决方案。

邓邓子的博客 8113

socket、sock、sk_buff的关系

在Linux2.6中,struct sk_buf承担了socket的输入输出的传输缓存的任务。 首先,还是先看struct socket的定义 /** * struct socket - general BSD socket * @state: socket state (%SS_CONNECTED, etc) * @type: socket type (...

Tasdily的博客 2002

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

struct sk_buff 结构

struct sk_buff可能是linux网络代码中最重要的数据结构,它表示接收或发送数据包的包头信息,并包含很多成员变量供网络代码中的各子系统使用。 这个结构被网络的不同层(MAC或者其他二层链路协议,三层的IP,四层的TCP或UDP等)使用,并且其中的成员变量在结构从一层向另一层传递时改变。 L4向L3传递前会添加一个L4的头部,同样,L3向L2传递前,会添加一个L3...

LetsFl1988的专栏 281

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

struct sk_buff(2)

 LINUX网络协议栈实现分析(-)SKBUFF的实现本文是我尝试分析LINUX网络协议栈实现的一系列文章中的第一篇,主要分析LINUX网络协议栈中SKBUFF的实现。分析以LINUX2.2.x为基础,同时也包括了相同的描述对象在LINUX2.4.x中的新变化。本文引用的代码的版本分别是:LINUX2.2.25,LINUX2.4.20。1 简介了解网络协议栈的人都知道,网络协议栈是一个有层次的软件

thomasCJ的专栏 1629

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

struct sk_buff分析

作者:独孤九贱struct sk_buff {<br />    struct sk_buff    * next;                <br />         struct sk_buff    * prev;            <br />    struct sk_buff_head * list;        <br />    struct sock    *sk;            <br />    struct timeval    stamp;          

zhangx105504的专栏 706

source insight4.0 内核源码 skb sk_buff 结构体 struct_group 不识别问题解决

struct_group 通过宏创造了一种对开发者很友好的“语法糖”,但它所依赖的匿名结构体、变长参数等特性,恰恰踩中了 Source Insight 这种非编译型、基于模式匹配的解析器的多个“软肋”。这也就是为什么即便添加了 C.tom 规则,有时仍无法完美解决所有解析问题的根本原因——因为工具的核心处理逻辑,在面对这种“宏魔法”时存在天然的局限性。

小狗爱吃黄桃罐头的博客 199

struct sk_buff

struct sk_buff {     struct sk_buff      *next;     struct sk_buff      *prev;     struct sock         *sk;         //被哪个sock所拥有     struct skb_timeval -tstamp;      //数据包到达的时间     struct net_dev

jiangdewu的专栏 764

skb学习记录-----sk_buff封装过程

1、创建skb struct sk_buff *skb = NULL; skb = alloc_skb(500, GFP_ATOMIC);//第一个参数是要分配的数据段长度,即skb->data - skb->tail;下图是分配空间后相关指针的位置 2、初始定位 static inline void skb_reserve(struct sk_buff *skb, int len)...

weixin_43483685的博客 2821
上一篇: 2020-08-07
下一篇: Linux网络协议栈(一)——Socket入门
TuxedoLinux
博客等级 码龄9年 268粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值