skbuff_head_cache去哪里了



在定位linux网络系统内存使用相关问题的时候,/proc/slabinfo下的"skbuff_head_cache""skbuff_fclone_cache"是常用的工具,但是有时候你会发现在/proc/slabinfo没有这两个条目,本文记录了其原因以及如何确认对应的sk_buffer结构的大致数量。

         如何找到对应的slabinfo

找不到"skbuff_head_cache""skbuff_fclone_cache"的原因是在CONFIG_SLUB这个配置下,memory cache可能被合并使用。

 

访问/sys/kernel/slab/xxxxxx为要找的slabinfo的名称,在这个目录下可以看到该slab所共用的memory cache的信息(各个共用该slabmemory cache的统计数据之和)

 

找到所有的共用者的方法:在/sys/kernel/slab目录使用”ll | grep xxx”,你会看到要找的slabinfo被链接到一个目录,名为”:t-xxx”(其中xxx为该slab大小),这个目录对应的就是真实的slabinfo,通过ll命令查看有哪些slab链接到该目录(:t-0000512这种),这些目录对应的就是共用该kmem_cache结构的所有memory cache(他们中的一个会出现在/proc/slabinfo中,查看对应的目录也可以得到相关信息)

         相关内核实现

  1. 首先确认你的系统编译的时候,打开的是CONFIG_SLUB这个选项,CONFIG_SLABCONFIG_SLOB不会存在这个问题。

  2. 在打开了CONFIG_SLUB的情况下,内核在创建memory cache结构的时候,会先看能否使用已有的struct kmem_cache结构,而不是对每次kmem_cache_create都生成独立的struct kmem_cache结构,这就造成了很多kmem_cache_create调用其实没有生成struct kmem_cache结构,在/proc/slabinfo中也就看不到了。具体如下:

    内核初始化的时候skb_init调用kmem_cache_create来创建"skbuff_head_cache""skbuff_fclone_cache"这两个memory cache,其执行的是mm/slub.c中定义的kmem_cache_create函数,看看该函数的实现就清楚了(其先通过find_mergeable看看有无现成的可以结构)

  3. 内核相关实现在kmem_cache_create->sysfs_slab_alias这个函数中完成,分析这个函数可以知道虽然共用了structkmem_cache结构,每次kmem_cache_create调用的时候,会在/sys/kernel/slab目录下用调用者提供的name创建一个目录,并把该目录链接到其共用的struct kmem_cache结构对应的目录结构中。

  4. slabinfo对应的proc目录名称命名方式见内核函数sysfs_slab_add,其命名方式为:对于unmergeable(具体条件见内核函数slab_unmergeable)的,直接使用kmem_cache_create调用传入的名字,否则使用create_unique_id创建有类型、大小决定的名字。

/** * __netdev_alloc_skb - allocate an skbuff for rx on a specific device * @dev: network device to receive on * @len: length to allocate * @gfp_mask: get_free_pages mask, passed to alloc_skb * * Allocate a new &sk_buff and assign it a usage count of one. The * buffer has NET_SKB_PAD headroom built in. Users should allocate * the headroom they think they need without accounting for the * built in space. The built in space is used for optimisations. * * %NULL is returned if there is no free memory. */ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int len, gfp_t gfp_mask) { struct page_frag_cache *nc; struct sk_buff *skb; bool pfmemalloc; void *data; len += NET_SKB_PAD; /* If requested length is either too small or too big, * we use kmalloc() for skb->head allocation. */ if (len <= SKB_WITH_OVERHEAD(1024) || len > SKB_WITH_OVERHEAD(PAGE_SIZE) || (gfp_mask & (__GFP_DIRECT_RECLAIM | GFP_DMA))) { skb = __alloc_skb(len, gfp_mask, SKB_ALLOC_RX, NUMA_NO_NODE); if (!skb) goto skb_fail; goto skb_success; } len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); len = SKB_DATA_ALIGN(len); if (sk_memalloc_socks()) gfp_mask |= __GFP_MEMALLOC; if (in_hardirq() || irqs_disabled()) { nc = this_cpu_ptr(&netdev_alloc_cache); data = page_frag_alloc(nc, len, gfp_mask); pfmemalloc = nc->pfmemalloc; } else { local_bh_disable(); nc = this_cpu_ptr(&napi_alloc_cache.page); data = page_frag_alloc(nc, len, gfp_mask); pfmemalloc = nc->pfmemalloc; local_bh_enable(); } if (unlikely(!data)) return NULL; skb = __build_skb(data, len); if (unlikely(!skb)) { skb_free_frag(data); return NULL; } if (pfmemalloc) skb->pfmemalloc = 1; skb->head_frag = 1; skb_success: skb_reserve(skb, NET_SKB_PAD); skb->dev = dev; skb_fail: return skb; } EXPORT_SYMBOL(__netdev_alloc_skb);这里返回的指针指向skb的头还是data部分
最新发布
03-21
<think>好的,我现在需要回答用户的问题:在提供的`__netdev_alloc_skb`函数中,返回的`sk_buff`指针是指向`skb`的头还是`data`部分? 首先,我得先理解这个函数的整体结构和流程。用户给出的代码是Linux内核中分配skb的函数,用于网络设备接收数据。函数名是`__netdev_alloc_skb`,根据注释,它的作用是分配一个skb,供特定设备接收数据使用。 用户的问题集中在返回的指针是skb的头还是data部分。要解决这个问题,我需要分析代码中如何构建skb,尤其是`__build_skb`和`skb_reserve`这两个关键点。 首先,在代码中,当满足某些条件时(比如长度不超过1024或者大于PAGE_SIZE,或者特定的内存分配标志),会使用`__alloc_skb`来分配skb。否则,会通过`page_frag_alloc`分配内存,然后调用`__build_skb`来构建skb。无论哪种情况,最后都会调用`skb_reserve(skb, NET_SKB_PAD)`,将data指针向后移动NET_SKB_PAD的大小。 接下来,我需要回忆`struct sk_buff`的结构。skb通常包含指向数据区的指针,比如`head`和`data`。`head`指向分配的内存块的起始位置,而`data`指向实际的数据起始位置。`skb_reserve`函数的作用就是调整data指针的位置,通常在head之后预留一定的空间,这里就是NET_SKB_PAD的大小。 现在,当函数返回`skb`指针时,这个指针指向的是整个`sk_buff`结构体,而不是直接的数据区。但用户的问题可能是在问,返回的skb的data指针的位置在哪里,或者说返回的skb的head和data的关系。不过根据问题,用户明确问的是返回的指针指向头还是data部分,这里的指针是`struct sk_buff *`,也就是skb结构本身的指针,而不是data的指针。 不过可能用户存在误解,认为返回的指针直接指向数据区,但实际上,skb是一个结构体,里面包含了指向数据区的指针。因此,返回的指针是skb结构体的指针,而data部分是通过skb->data来访问的。不过需要确认代码中的`__build_skb`和`skb_reserve`如何影响这些指针。 例如,`__build_skb`函数会使用预先分配的data内存来构建skb结构,此时skb的head和data指针都会被设置。然后调用`skb_reserve`会调整data指针,使其向后移动NET_SKB_PAD字节,从而在head和data之间留出空间。因此,返回的skb结构体的data指针指向的是实际数据区的开始位置,而head指向的是分配的内存块的起始位置。 所以,用户的问题的答案应该是:返回的指针是skb结构体的指针,而data部分是通过skb->data访问的。在函数中,通过`skb_reserve`,data指针被调整到预留了NET_SKB_PAD之后的位置,因此实际的数据区是从skb->data开始的,而整个分配的内存块起始于skb->head。因此,返回的skb指针指向的是结构体本身,而不是直接的数据区。但可能用户的问题更关心的是数据区的起始位置,即当返回skb后,数据是从head加上预留空间开始的。需要明确回答这一点。 另外,需要注意,`__build_skb`的data参数是内存块的起始地址,构建后的skb的head和data指针都指向这个地址。然后`skb_reserve`会将data指针后移,head保持不变。因此,最终的数据区是head + NET_SKB_PAD的位置,而结构体skb的指针指向的是整个结构体,不是直接的数据区。因此,用户的问题的答案应该是返回的指针指向skb结构体的头,而data部分是结构体中的一个字段,指向预留后的位置。需要明确说明这一点。</think>以下是对`__netdev_alloc_skb`函数返回的`sk_buff`指针的详细解释: --- ### 一、核心结论 **返回的指针指向`sk_buff`结构体本身的头部(即结构体对象),而非直接指向数据区(`data`部分)。** 实际数据区需要通过`skb->data`访问,且该数据区的位置已通过`skb_reserve`预留了`NET_SKB_PAD`头部空间。 --- ### 二、关键代码流程分析 #### 1. 内存分配与`skb`构建 - **通过`__build_skb`或`__alloc_skb`创建`sk_buff`对象** 无论是通过页片段分配(`page_frag_alloc`)还是直接分配(`__alloc_skb`),最终会生成一个`sk_buff`结构体对象,并关联到预分配的内存块。 - `__build_skb(data, len)`:将已分配的`data`内存块与`sk_buff`结构体绑定。 - `skb->head`指向内存块起始地址(`data`参数的值)。 - `skb->data`初始时也指向内存块起始地址。 #### 2. 关键操作:`skb_reserve(skb, NET_SKB_PAD)` - **调整数据区指针** `skb_reserve`将`skb->data`指针后移`NET_SKB_PAD`字节,为协议头预留空间: ```c skb->data = skb->head + NET_SKB_PAD; skb->tail = skb->data; // 初始时数据区为空 ``` - **内存布局示例** ``` | skb->head (内存块起始地址) | → [NET_SKB_PAD预留区] | → skb->data (实际数据区起点) | ``` #### 3. 最终返回的指针 - **指向`sk_buff`结构体** 函数返回的是`struct sk_buff *skb`,即`sk_buff`对象自身的指针。 - 若要访问数据区,需通过`skb->data`。 - 数据区的物理起始位置为`skb->head + NET_SKB_PAD`。 --- ### 三、示例说明 假设调用`__netdev_alloc_skb`后获得`skb`指针: ```c struct sk_buff *skb = __netdev_alloc_skb(dev, len, gfp_mask); ``` - **`skb`指针本身** 指向`sk_buff`结构体(包含`head`、`data`、`tail`等字段)。 - **数据区访问方式** - 数据起始地址:`skb->data` - 预留空间:`skb->data - skb->head == NET_SKB_PAD`(值为32字节)。 --- ### 四、与`siwifi_ipc_rxbuf_frag_alloc`的对比 在之前的函数`siwifi_ipc_rxbuf_frag_alloc`中: - **直接操作物理地址** `buf->dma_addr`指向数据区的物理地址(`head + offset + NET_SKB_PAD`),供硬件DMA写入。 - **虚拟地址对称性** `buf->addr`对应`skb->data`,两者均跳过`NET_SKB_PAD`。 而在`__netdev_alloc_skb`中: - **通过`sk_buff`结构抽象** 数据区位置由`skb->data`管理,开发者无需直接操作底层内存地址。 --- ### 五、总结 | 关键点 | 说明 | |----------------------|----------------------------------------------------------------------| | **返回的指针类型** | `struct sk_buff *`,指向结构体对象,而非数据区。 | | **数据区访问** | 通过`skb->data`获取,已跳过`NET_SKB_PAD`预留空间。 | | **设计意图** | 封装内存布局细节,为协议栈提供统一接口,隐藏`NET_SKB_PAD`等底层优化。 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值