<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    Linux內(nèi)存管理slub分配器

    共 5736字,需瀏覽 12分鐘

     ·

    2020-09-15 13:05


    背景


    1. Kernel版本:4.14
    2. ARM64處理器,Contex-A53,雙核
    3. 使用工具:Source Insight 3.5, Visio

    1. 概述

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

    先來一個初印象:

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

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

    • struct kmem_cache:用于管理SLAB緩存,包括該緩存中對象的信息描述,per-CPU/Node管理slab頁面等;關(guān)鍵字段如下:
    /* * Slab cache management. */struct kmem_cache {	struct kmem_cache_cpu __percpu *cpu_slab;       //每個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;#endif	struct kmem_cache_order_objects oo;     //該結(jié)構(gòu)體會描述申請頁面的order值,以及object的個數(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 *); // 對象構(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最終會鏈接在一個全局鏈表中 struct kmem_cache_node *node[MAX_NUMNODES]; //Node管理slab頁面};
    • struct kmem_cache_cpu:用于管理每個CPU的slab頁面,可以使用無鎖訪問,提高緩存對象分配速度;
    struct kmem_cache_cpu {	void **freelist;	/* Pointer to next available object */                  //指向空閑對象的指針	unsigned long tid;	/* Globally unique transaction id */                	struct page *page;	/* The slab from which we are allocating */     //slab緩存頁面#ifdef CONFIG_SLUB_CPU_PARTIAL	struct page *partial;	/* Partially allocated frozen slabs */#endif#ifdef CONFIG_SLUB_STATS	unsigned stat[NR_SLUB_STAT_ITEMS];#endif};
    • struct kmem_cache_node:用于管理每個Node的slab頁面,由于每個Node的訪問速度不一致,slab頁面由Node來管理;
    /* * The slab lists for all objects. */struct kmem_cache_node {	spinlock_t list_lock;
    #ifdef CONFIG_SLUB unsigned long nr_partial; //slab頁表數(shù)量 struct list_head partial; //slab頁面鏈表#ifdef CONFIG_SLUB_DEBUG atomic_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)合體進行復(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_64BIT			int pages;	/* Nr of partial slabs left */			int pobjects;	/* Approximate # of objects */#else			short 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. 流程分析

    針對Slub的使用,可以從三個維度來分析:

    1. slub緩存創(chuàng)建
    2. slub對象分配
    3. slub對象釋放

    下邊將進一步分析。

    3.1 kmem_cache_create

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

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

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

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

    3. calculate_sizes函數(shù)會根據(jù)指定的force_order或根據(jù)對象大小去計算kmem_cache結(jié)構(gòu)體中的size/min/oo等值,其中kmem_cache_order_objects結(jié)構(gòu)體,是由頁面分配order值和對象數(shù)量兩者通過位域拼接起來的。

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

    3.2 kmem_cache_alloc

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

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

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

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

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

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



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


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


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


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


    3.2 kmem_cache_free

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

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

    效果如下:

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


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


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


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


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

    ?

    ? 推薦閱讀:
    ? ??專輯|Linux文章匯總
    ? ??專輯|程序人生
    ? ??專輯|C語言


    嵌入式Linux
    微信掃描二維碼,關(guān)注我的公眾號?
    瀏覽 102
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報
    評論
    圖片
    表情
    推薦
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    午夜欧美精品久久久 | 人人色人人色人人色 | 久久大香蕉视频 | 成人做爰A片免费看网站找不到了 | 一级片视频在线观看大全 |