日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux内存管理slub分配器

發(fā)布時(shí)間:2023/12/20 linux 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux内存管理slub分配器 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

背景

  • Kernel版本:4.14

  • ARM64處理器,Contex-A53,雙核

  • 使用工具:Source Insight 3.5, Visio

  • 1. 概述

    之前的文章分析的都是基于頁面的內(nèi)存分配,而小塊內(nèi)存的分配和管理是通過塊分配器來實(shí)現(xiàn)的。目前內(nèi)核中,有三種方式來實(shí)現(xiàn)小塊內(nèi)存分配:slab, slub, slob,最先有slab分配器,slub/slob分配器是改進(jìn)版,slob分配器適用于小內(nèi)存嵌入式設(shè)備,而slub分配器目前已逐漸成為主流塊分配器。接下來的文章,就是以slub分配器為目標(biāo),進(jìn)一步深入。

    先來一個(gè)初印象:

    2. 數(shù)據(jù)結(jié)構(gòu)

    有四個(gè)關(guān)鍵的數(shù)據(jù)結(jié)構(gòu):

    • struct kmem_cache:用于管理SLAB緩存,包括該緩存中對(duì)象的信息描述,per-CPU/Node管理slab頁面等;關(guān)鍵字段如下:

    /** Slab cache management.*/ struct kmem_cache {struct kmem_cache_cpu __percpu *cpu_slab; //每個(gè)CPU slab頁面/* Used for retriving partial slabs etc */unsigned long flags;unsigned long min_partial;int size; /* The size of an object including meta data */int object_size; /* The size of an object without meta data */int offset; /* Free pointer offset. */ #ifdef CONFIG_SLUB_CPU_PARTIAL/* Number of per cpu partial objects to keep around */unsigned int cpu_partial; #endifstruct kmem_cache_order_objects oo; //該結(jié)構(gòu)體會(huì)描述申請(qǐng)頁面的order值,以及object的個(gè)數(shù)/* Allocation and freeing of slabs */struct kmem_cache_order_objects max;struct kmem_cache_order_objects min;gfp_t allocflags; /* gfp flags to use on each alloc */int refcount; /* Refcount for slab cache destroy */void (*ctor)(void *); // 對(duì)象構(gòu)造函數(shù)int inuse; /* Offset to metadata */int align; /* Alignment */int reserved; /* Reserved bytes at the end of slabs */int red_left_pad; /* Left redzone padding size */const char *name; /* Name (only for display!) */struct list_head list; /* List of slab caches */ //kmem_cache最終會(huì)鏈接在一個(gè)全局鏈表中struct kmem_cache_node *node[MAX_NUMNODES]; //Node管理slab頁面 };
    • struct kmem_cache_cpu:用于管理每個(gè)CPU的slab頁面,可以使用無鎖訪問,提高緩存對(duì)象分配速度;

    struct kmem_cache_cpu {void **freelist; /* Pointer to next available object */ //指向空閑對(duì)象的指針unsigned long tid; /* Globally unique transaction id */ struct page *page; /* The slab from which we are allocating */ //slab緩存頁面 #ifdef CONFIG_SLUB_CPU_PARTIALstruct page *partial; /* Partially allocated frozen slabs */ #endif #ifdef CONFIG_SLUB_STATSunsigned stat[NR_SLUB_STAT_ITEMS]; #endif };
    • struct kmem_cache_node:用于管理每個(gè)Node的slab頁面,由于每個(gè)Node的訪問速度不一致,slab頁面由Node來管理;

    /** The slab lists for all objects.*/ struct kmem_cache_node {spinlock_t list_lock;#ifdef CONFIG_SLUBunsigned long nr_partial; //slab頁表數(shù)量struct list_head partial; //slab頁面鏈表 #ifdef CONFIG_SLUB_DEBUGatomic_long_t nr_slabs;atomic_long_t total_objects;struct list_head full; #endif #endif };
    • struct page:用于描述slab頁面,struct page結(jié)構(gòu)體中很多字段都是通過union聯(lián)合體進(jìn)行復(fù)用的。struct page結(jié)構(gòu)中,用于slub的成員如下:

    struct page {union {...void *s_mem; /* slab first object */...};/* Second double word */union {...void *freelist; /* sl[aou]b first free object */...};union {...struct {union {...struct { /* SLUB */unsigned inuse:16;unsigned objects:15;unsigned frozen:1;};...};...}; }; /** Third double word block*/union {...struct { /* slub per cpu partial pages */struct page *next; /* Next partial slab */ #ifdef CONFIG_64BITint pages; /* Nr of partial slabs left */int pobjects; /* Approximate # of objects */ #elseshort int pages;short int pobjects; #endif};struct rcu_head rcu_head; /* Used by SLAB* when destroying via RCU*/};...struct kmem_cache *slab_cache; /* SL[AU]B: Pointer to slab */ ... }

    圖來了:

    3. 流程分析

    針對(duì)Slub的使用,可以從三個(gè)維度來分析:

  • slub緩存創(chuàng)建

  • slub對(duì)象分配

  • slub對(duì)象釋放

  • 下邊將進(jìn)一步分析。

    3.1 kmem_cache_create

    在內(nèi)核中通過kmem_cache_create接口來創(chuàng)建一個(gè)slab緩存。

    先看一下這個(gè)接口的函數(shù)調(diào)用關(guān)系圖:

  • kmem_cache_create完成的功能比較簡(jiǎn)單,就是創(chuàng)建一個(gè)用于管理slab緩存的kmem_cache結(jié)構(gòu),并對(duì)該結(jié)構(gòu)體進(jìn)行初始化,最終添加到全局鏈表中。kmem_cache結(jié)構(gòu)體初始化,包括了上文中分析到的kmem_cache_cpu和kmem_cache_node兩個(gè)字段結(jié)構(gòu)。

  • 在創(chuàng)建的過程中,當(dāng)發(fā)現(xiàn)已有的slab緩存中,有存在對(duì)象大小相近,且具有兼容標(biāo)志的slab緩存,那就只需要進(jìn)行merge操作并返回,而無需進(jìn)一步創(chuàng)建新的slab緩存。

  • calculate_sizes函數(shù)會(huì)根據(jù)指定的force_order或根據(jù)對(duì)象大小去計(jì)算kmem_cache結(jié)構(gòu)體中的size/min/oo等值,其中kmem_cache_order_objects結(jié)構(gòu)體,是由頁面分配order值和對(duì)象數(shù)量?jī)烧咄ㄟ^位域拼接起來的。

  • 在創(chuàng)建slab緩存的時(shí)候,有一個(gè)先雞后蛋的問題:kmem_cache結(jié)構(gòu)體來管理一個(gè)slab緩存,而創(chuàng)建kmem_cache結(jié)構(gòu)體又是從slab緩存中分配出來的對(duì)象,那么這個(gè)問題是怎么解決的呢?可以看一下kmem_cache_init函數(shù),內(nèi)核中定義了兩個(gè)靜態(tài)的全局變量kmem_cache和kmem_cache_node,在kmem_cache_init函數(shù)中完成了這兩個(gè)結(jié)構(gòu)體的初始化之后,相當(dāng)于就是創(chuàng)建了兩個(gè)slab緩存,一個(gè)用于分配kmem_cache結(jié)構(gòu)體對(duì)象的緩存池,一個(gè)用于分配kmem_cache_node結(jié)構(gòu)體對(duì)象的緩存池。由于kmem_cache_cpu結(jié)構(gòu)體是通過__alloc_percpu來分配的,因此不需要?jiǎng)?chuàng)建一個(gè)相關(guān)的slab緩存。

  • 3.2 kmem_cache_alloc

    kmem_cache_alloc接口用于從slab緩存池中分配對(duì)象。

    看一下大體的調(diào)用流程圖:

    從上圖中可以看出,分配slab對(duì)象與Buddy System中分配頁面類似,存在快速路徑和慢速路徑兩種,所謂的快速路徑就是per-CPU緩存,可以無鎖訪問,因而效率更高。

    整體的分配流程大體是這樣的:優(yōu)先從per-CPU緩存中進(jìn)行分配,如果per-CPU緩存中已經(jīng)全部分配完畢,則從Node管理的slab頁面中遷移slab頁到per-CPU緩存中,再重新分配。當(dāng)Node管理的slab頁面也不足的情況下,則從Buddy System中分配新的頁面,添加到per-CPU緩存中。

    還是用圖來說明更清晰,分為以下幾步來分配:

    • fastpath快速路徑下,以原子的方式檢索per-CPU緩存的freelist列表中的第一個(gè)對(duì)象,如果freelist為空并且沒有要檢索的對(duì)象,則跳入慢速路徑操作,最后再返回到快速路徑中重試操作。

    • slowpath-1將per-CPU緩存中page指向的slab頁中的空閑對(duì)象遷移到freelist中,如果有空閑對(duì)象,則freeze該頁面,沒有空閑對(duì)象則跳轉(zhuǎn)到slowpath-2。

    • slowpath-2將per-CPU緩存中partial鏈表中的第一個(gè)slab頁遷移到page指針中,如果partial鏈表為空,則跳轉(zhuǎn)到slowpath-3。

    • slowpath-3將Node管理的partial鏈表中的slab頁遷移到per-CPU緩存中的page中,并重復(fù)第二個(gè)slab頁將其添加到per-CPU緩存中的partial鏈表中。如果遷移的slab中空閑對(duì)象超過了kmem_cache.cpu_partial的一半,則僅遷移slab頁,并且不再重復(fù)。如果每個(gè)Node的partial鏈表都為空,跳轉(zhuǎn)到slowpath-4。

    • slowpath-4從Buddy System中獲取頁面,并將其添加到per-CPU的page中。

    3.2 kmem_cache_free

    kmem_cache_free的操作,可以看成是kmem_cache_alloc的逆過程,因此也分為快速路徑和慢速路徑兩種方式,同時(shí),慢速路徑中又分為了好幾種情況,可以參考kmem_cache_alloc的過程。

    調(diào)用流程圖如下:

    效果如下:

    • 快速路徑釋放 快速路徑下,直接將對(duì)象返回到freelist中即可。

    • put_cpu_partialput_cpu_partial函數(shù)主要是將一個(gè)剛freeze的slab頁,放入到partial鏈表中。在put_cpu_partial函數(shù)中調(diào)用unfreeze_partials函數(shù),這時(shí)候會(huì)將per-CPU管理的partial鏈表中的slab頁面添加到Node管理的partial鏈表的尾部。如果超出了Node的partial鏈表,溢出的slab頁面中沒有分配對(duì)象的slab頁面將會(huì)返回到伙伴系統(tǒng)。

    • add_partial 添加slab頁到Node的partial鏈表中。

    • remove_partial 從Node的partial鏈表移除slab頁。

    具體釋放的流程走哪個(gè)分支,跟對(duì)象的使用情況,partial鏈表的個(gè)數(shù)nr_partial/min_partial等相關(guān),細(xì)節(jié)就不再深入分析了。

    ?

    ? 推薦閱讀:

    ? ??專輯|Linux文章匯總

    ? ??專輯|程序人生

    ? ??專輯|C語言

    嵌入式Linux

    微信掃描二維碼,關(guān)注我的公眾號(hào)?

    總結(jié)

    以上是生活随笔為你收集整理的Linux内存管理slub分配器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。