slab下kmalloc內核函數實現

文章目錄

  • kmalloc的整體實現
  • 獲取高速緩存
    • 高速緩存
    • 獲取index
  • 總結

https://blog.csdn.net/qq_41683305/article/details/124554490,在這篇文章中,我們介紹了伙伴算法、slab機制和常見的內存管理函數,接下來,我們看看kmalloc內核函數的具體實現。

kmalloc() 分配連續的物理地址,用于小內存分配。get_free_page() 分配連續的物理地址,用于整頁分配。在slab機制下(還有slob、slub),kmalloc() 函數是基于 slab 實現的。slab 是為分配小內存提供的一種高效機制。但 slab 這種分配機制又不是獨立的,它本身也是在頁分配器的基礎上來劃分更細粒度的內存供調用者使用。也就是說系統先用頁分配器分配以頁為最小單位的連續物理地址,然后 kmalloc() 再在這上面根據調用者的需要進行切分。

我們可以調用kmalloc獲取slab中未分配的對象,比如kmalloc(30,GFP_KERNAL),系統會從slab中分配一個32字節的對象。

kmalloc的整體實現

kmalloc的實現如下:

static __always_inline void *kmalloc(size_t size, gfp_t flags)
{if (__builtin_constant_p(size)) {if (size > KMALLOC_MAX_CACHE_SIZE)return kmalloc_large(size, flags);
#ifndef CONFIG_SLOBif (!(flags & GFP_DMA)) {int index = kmalloc_index(size);if (!index)return ZERO_SIZE_PTR;return kmem_cache_alloc_trace(kmalloc_caches[index],flags, size);}
#endif}return __kmalloc(size, flags);
}
  • __builtin_constant_p表示傳入的是否為一個實數,gcc編譯器會做這個判斷,如果是一個確定的實數而非變量,那么它返回true,主要用于編譯優化的處理。
  • 如果是實數,那么會判斷size是否大于KMALLOC_MAX_CACHE_SIZE,此值表示的是系統創建的slab cache(slab創建的對象)的最大值,系統為kmalloc預先創建了很多大小不同的kmem cache,用于內存分配。這里的含義就是如果內存申請超過此值,那么直接使用 kmalloc_large進行大內存分配,實際上最終會調用頁分配器去分配內存,而不是使用slab分配器
  • 如果沒有大于KMALLOC_MAX_CACHE_SIZE,會調用__kmalloc來進行分配內存。

我們看的是slab下kmalloc的實現,所以。我們看mm/slab.c的__kmalloc,slob.c,slub.c也有該函數的實現。
在這里插入圖片描述

void *__kmalloc(size_t size, gfp_t flags)
{return __do_kmalloc(size, flags, _RET_IP_);
}/*** __do_kmalloc - allocate memory* @size: how many bytes of memory are required.* @flags: the type of memory to allocate (see kmalloc).* @caller: function caller for debug tracking of the caller*/
static __always_inline void *__do_kmalloc(size_t size, gfp_t flags,unsigned long caller)
{struct kmem_cache *cachep;void *ret;if (unlikely(size > KMALLOC_MAX_CACHE_SIZE))return NULL;cachep = kmalloc_slab(size, flags);if (unlikely(ZERO_OR_NULL_PTR(cachep)))return cachep;ret = slab_alloc(cachep, flags, caller);kasan_kmalloc(cachep, ret, size, flags);trace_kmalloc(caller, ret,size, cachep->size, flags);return ret;
}

在__do_kmalloc會先調用kmalloc_slab找到一個高速緩存cachep,然后調用slab_alloc從高速緩存cachep分配一個對象,最后返回。kasan_kmalloc和trace_kmalloc是內存調試用的,我們可以忽略。

獲取高速緩存

接下來我們分析怎么得到一個高速緩存的,kmalloc_slab的實現在mm/slab_common.c中。

/** Find the kmem_cache structure that serves a given size of* allocation*/
struct kmem_cache *kmalloc_slab(size_t size, gfp_t flags)
{int index;if (size <= 192) {if (!size)return ZERO_SIZE_PTR;index = size_index[size_index_elem(size)];} else {if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) {WARN_ON(1);return NULL;}index = fls(size - 1);}#ifdef CONFIG_ZONE_DMAif (unlikely((flags & GFP_DMA)))return kmalloc_dma_caches[index];#endifreturn kmalloc_caches[index];
}

該函數會首先獲得一個索引index,然后返回kmalloc_caches[index],說明kmalloc_caches[index]就是我們需要的高速緩存,kmalloc_caches是系統已經分配好的高速緩存,和slab機制中的高速緩存對應。

高速緩存

struct kmem_cache *kmalloc_caches[KMALLOC_SHIFT_HIGH + 1];

KMALLOC_SHIFT_HIGH 的值在slab、slob、slub下不同,我們看slab下所定義的值:

#define KMALLOC_SHIFT_HIGH	((MAX_ORDER + PAGE_SHIFT - 1) <= 25 ? \(MAX_ORDER + PAGE_SHIFT - 1) : 25)/* Free memory management - zoned buddy allocator.  */
#ifndef CONFIG_FORCE_MAX_ZONEORDER
#define MAX_ORDER 11
#else
#define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER
#endif/* PAGE_SHIFT determines the page size */
#define PAGE_SHIFT		12

如果沒有定義CONFIG_FORCE_MAX_ZONEORDER,MAX_ORDER 是11,代表的是伙伴算法的11個塊鏈表,MAX_ORDER 決定伙伴算法中的塊鏈表數,PAGE_SHIFT與具體架構有關,我們看的是arm下的大小,PAGE_SHIFT 代表的是頁大小,12位,共4KB,所以在arm下一個頁大小為4KB。
在這種情況下,KMALLOC_SHIFT_HIGH 的大小是22。

kmalloc_caches是內核自己生成的,通過調用create_kmalloc_caches函數


/** Create the kmalloc array. Some of the regular kmalloc arrays* may already have been created because they were needed to* enable allocations for slab creation.*/
void __init create_kmalloc_caches(unsigned long flags)
{int i;for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) {if (!kmalloc_caches[i])new_kmalloc_cache(i, flags);/** Caches that are not of the two-to-the-power-of size.* These have to be created immediately after the* earlier power of two caches*/if (KMALLOC_MIN_SIZE <= 32 && !kmalloc_caches[1] && i == 6)new_kmalloc_cache(1, flags);if (KMALLOC_MIN_SIZE <= 64 && !kmalloc_caches[2] && i == 7)new_kmalloc_cache(2, flags);}/* Kmalloc array is now usable */slab_state = UP;#ifdef CONFIG_ZONE_DMAfor (i = 0; i <= KMALLOC_SHIFT_HIGH; i++) {struct kmem_cache *s = kmalloc_caches[i];if (s) {int size = kmalloc_size(i);char *n = kasprintf(GFP_NOWAIT,"dma-kmalloc-%d", size);BUG_ON(!n);kmalloc_dma_caches[i] = create_kmalloc_cache(n,size, SLAB_CACHE_DMA | flags);}}
#endif
}

KMALLOC_SHIFT_LOW在slab中定義的是5

create_kmalloc_caches調用new_kmalloc_cache,new_kmalloc_cache調用create_kmalloc_cache最終創建高速緩存

static void __init new_kmalloc_cache(int idx, unsigned long flags)
{kmalloc_caches[idx] = create_kmalloc_cache(kmalloc_info[idx].name,kmalloc_info[idx].size, flags);
}struct kmem_cache *__init create_kmalloc_cache(const char *name, size_t size,unsigned long flags)
{struct kmem_cache *s = kmem_cache_zalloc(kmem_cache, GFP_NOWAIT);if (!s)panic("Out of memory when creating slab %s\n", name);create_boot_cache(s, name, size, flags);list_add(&s->list, &slab_caches);s->refcount = 1;return s;
}

kmalloc_info有25個,定義了25個高速緩存的名字和大小

/** kmalloc_info[] is to make slub_debug=,kmalloc-xx option work at boot time.* kmalloc_index() supports up to 2^26=64MB, so the final entry of the table is* kmalloc-67108864.*/
static struct {const char *name;unsigned long size;
} const kmalloc_info[] __initconst = {{NULL,                      0},		{"kmalloc-96",             96},{"kmalloc-192",           192},		{"kmalloc-8",               8},{"kmalloc-16",             16},		{"kmalloc-32",             32},{"kmalloc-64",             64},		{"kmalloc-128",           128},{"kmalloc-256",           256},		{"kmalloc-512",           512},{"kmalloc-1024",         1024},		{"kmalloc-2048",         2048},{"kmalloc-4096",         4096},		{"kmalloc-8192",         8192},{"kmalloc-16384",       16384},		{"kmalloc-32768",       32768},{"kmalloc-65536",       65536},		{"kmalloc-131072",     131072},{"kmalloc-262144",     262144},		{"kmalloc-524288",     524288},{"kmalloc-1048576",   1048576},		{"kmalloc-2097152",   2097152},{"kmalloc-4194304",   4194304},		{"kmalloc-8388608",   8388608},{"kmalloc-16777216", 16777216},		{"kmalloc-33554432", 33554432},{"kmalloc-67108864", 67108864}
};

獲取index

如果size小于等于192,index = size_index[size_index_elem(size)];

/* 8字節對齊 */
static inline int size_index_elem(size_t bytes)
{return (bytes - 1) / 8;
}/** Conversion table for small slabs sizes / 8 to the index in the* kmalloc array. This is necessary for slabs < 192 since we have non power* of two cache sizes there. The size of larger slabs can be determined using* fls.*/
static s8 size_index[24] = {3,	/* 8 */4,	/* 16 */5,	/* 24 */5,	/* 32 */6,	/* 40 */6,	/* 48 */6,	/* 56 */6,	/* 64 */1,	/* 72 */1,	/* 80 */1,	/* 88 */1,	/* 96 */7,	/* 104 */7,	/* 112 */7,	/* 120 */7,	/* 128 */2,	/* 136 */2,	/* 144 */2,	/* 152 */2,	/* 160 */2,	/* 168 */2,	/* 176 */2,	/* 184 */2	/* 192 */
};

如果size大于192,index = fls(size - 1);fls函數返回的是size-1的高位0的個數,比如0x800000,返回的是0,如果是0x00000000,返回的就是32。

總結

kmalloc獲取內存步驟如下:

  1. 判斷分配的內存是不是大于slab所能分配對象的最大值,如果大于則調用kmalloc_large,使用頁分配器分配,小于等于使用slab分配器分配內存,調用__kmalloc函數。
  2. __kmalloc調用__do_kmalloc來實現
  3. 在__do_kmalloc會先調用kmalloc_slab分配一個高速緩存,然后調用slab_alloc從高速緩存中分配一個對象,最后將該對象的首地址返回就是kmalloc獲取的內存

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/379529.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/379529.shtml
英文地址,請注明出處:http://en.pswp.cn/news/379529.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

PHP array_merge_recursive()函數與示例

PHP array_merge_recursive()函數 (PHP array_merge_recursive() function) array_merge_recursive() function is used to merge two or more arrays, it returns a new array with merged elements. The only difference between array_merge() and array_merge_recursive() …

標題:三羊獻瑞

標題&#xff1a;觀察下面的加法算式&#xff1a; 其中&#xff0c;相同的漢字代表相同的數字&#xff0c;不同的漢字代表不同的數字。 請你填寫“三羊獻瑞”所代表的4位數字&#xff08;答案唯一&#xff09;&#xff0c;不要填寫任何多余內容。 思路分析&#xff1a; 首先…

hdu 1069

地址&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid1069 題意&#xff1a;給定若干個木塊長寬高&#xff0c;長寬高可以自己調整&#xff0c;求堆積起來最高的高度。 mark&#xff1a;枚舉所有木塊長寬高可能情況&#xff0c;簡單dp。 代碼&#xff1a; #include <…

簡明 Python 編程規范

簡明 Python 編程規范編碼 所有的 Python 腳本文件都應在文件頭標上 # -*- coding:utf-8 -*- 。設置編輯器&#xff0c;默認保存為 utf-8 格式。注釋 業界普遍認同 Python 的注釋分為兩種的概念&#xff0c;一種是由 # 開頭的“真正的”注釋&#xff0c;另一種是 docstri…

進程虛擬地址管理

文章目錄1 地址分布實際使用中的內存區域2 進程的虛擬地址描述用戶空間mmap線程之間共享內存地址的實現機制1 地址分布 現在采用虛擬內存的操作系統通常都使用平坦地址空間&#xff0c;平坦地址空間是指地址空間范圍是一個獨立的連續空間&#xff08;比如&#xff0c;地址從0擴…

java兩個文件夾比較路徑_比較Java中兩個文件的路徑

java兩個文件夾比較路徑Given the paths of the two files and we have two compare the paths of the files in Java. 給定兩個文件的路徑&#xff0c;我們有兩個比較Java中文件的路徑。 Comparing paths of two files 比較兩個文件的路徑 To compare the paths of two file…

標題:加法變乘法

標題&#xff1a;我們都知道&#xff1a;123 … 49 1225 現在要求你把其中兩個不相鄰的加號變成乘號&#xff0c;使得結果為2015 比如&#xff1a; 123…10*1112…27*2829…49 2015 就是符合要求的答案。 請你尋找另外一個可能的答案&#xff0c;并把位置靠前的那個乘號左…

C# winform對話框用法大全收藏

對話框中我們常用了以下幾種&#xff1a; 1、文件對話框(FileDialog) 它又常用到兩個&#xff1a; 打開文件對話框(OpenFileDialog) 保存文件對話(SaveFileDialog) 2、字體對話框(FontDialog) 3、顏色對話框(&#xff23;olorDialog) 4、打印預瀏對話框(PrintPreviewDialog) 5、…

【翻譯】eXpressAppFramework QuickStart 業務模型設計(四)—— 實現自定義業務類...

這一講&#xff0c;你將學到如何從頭開始實現業務類。為此&#xff0c;將要實現Department和Position業務類。這些類將被應用到之前實現的Contact類中。你將學到引用對象自動生成用戶界面的基本要素。 在此之前&#xff0c;我建議你去閱讀一下 【翻譯】eXpressAppFramework Qui…

內存重映射

文章目錄1 kmap2 映射內核內存到用戶空間使用remap_pfn_range使用io_remap_pfn_rangemmap文件操作建立VMA和實際物理地址的映射mmap 之前分配 一次性映射mmap 之前分配 Page FaultPage Fault 中分配 映射內核內存有時需要重新映射&#xff0c;無論是從內核到用戶空間還是從內…

math.sqrt 有問題_JavaScript中帶有示例的Math.sqrt()方法

math.sqrt 有問題JavaScript | Math.sqrt()方法 (JavaScript | Math.sqrt() Method) The Math.sqrt() method is inbuilt in JavaScript to find the square root of a number. In this tutorial, we will learn about the sqrt() method with examples. JavaScript中內置了Mat…

標題:移動距離

標題&#xff1a;移動距離 X星球居民小區的樓房全是一樣的&#xff0c;并且按矩陣樣式排列。其樓房的編號為1,2,3… 當排滿一行時&#xff0c;從下一行相鄰的樓往反方向排號。 比如&#xff1a;當小區排號寬度為6時&#xff0c;開始情形如下&#xff1a; 1 2 3 4 5 6 12 11 1…

ISAPI Rewrite 實現簡單url重寫、二級域名重寫

實現步驟&#xff1a; 第一步&#xff1a;下載ISAPI_Rewrite.rar&#xff0c;將Rewrite文件夾和httpd.ini直接放在項目根目錄下面。 第二步&#xff1a;IIS配置&#xff0c;篩選Rewrite文件夾里面的Rewrite.dll文件&#xff0c;如圖&#xff1a; 第三步&#xff1a;在httpd.ini…

用戶登錄

用戶登錄 代碼namespace 用戶登錄 {public partial class Form1 : Form{public Form1(){InitializeComponent();}bool b1, b2, b3, b4, b5, b6;private void button1_Click(object sender, EventArgs e){try{if (b1 && b2 && b3 && b4 && b5 &…

進程上下文和中斷上下文

文章目錄進程的preempt_count變量thread_infopreempt_counthardirq相關softirq相關上下文原文鏈接&#xff1a; https://zhuanlan.zhihu.com/p/88883239進程的preempt_count變量 thread_info 在內核中&#xff0c;上下文的設置和判斷接口可以參考 include/linux/preempt.h 文…

標題:湊算式

標題&#xff1a;湊算式 這個算式中AI代表19的數字&#xff0c;不同的字母代表不同的數字。 比如&#xff1a; 68/3952/714 就是一種解法&#xff0c; 53/1972/486 是另一種解法。 這個算式一共有多少種解法&#xff1f; 注意&#xff1a;你提交應該是個整數&#xff0c;不要…

匯編中imul_JavaScript中帶有示例的Math.imul()方法

匯編中imulJavaScript | Math.imul()方法 (JavaScript | Math.imul() Method) Math.imul() is a function in math library of JavaScript that is used to the 32-bit multiplication of the two values passed to it. It uses C-like semantics to find the multiplication. …

AFTER觸發器與INSTEAD OF觸發器的區別

INSTEAD OF 觸發器用來代替通常的觸發動作&#xff0c;即當對表進行INSERT、UPDATE 或 DELETE 操作時&#xff0c;系統不是直接對表執行這些操作&#xff0c;而是把操作內容交給觸發器&#xff0c;讓觸發器檢查所進行的操作是否正確。如正確才進行相應的操作。因此&#xff0c;…

Linux內存地址管理

文章目錄系統內存布局內核地址的低端和高端內存概念低端內存高端內存地址轉換和MMULinux中的四級分頁模型虛擬地址字段頁表處理將虛擬地址轉換物理地址Linux系統中的每個內存地址都是虛擬的&#xff0c;它們不直接指向任何物理內存地址。每當訪問內存位置時&#xff0c;可以執行…

錄制caf 轉 mp3

編譯需要使用的 lame庫http://www.cocoachina.com/bbs/read.php?tid108237參考的文章http://blog.csdn.net/ysy441088327/article/details/7392842說起來&#xff0c;我一直在找一個音頻轉換成mp3的方法。一年前&#xff0c;我成功編譯出了一個lame for armv7的庫。苦于不會使…