伙伴算法、slab機制、內存管理函數

文章目錄

  • 1 伙伴算法
    • 頁框操作
      • alloc_pages()
  • 2 slab
    • slab機制要解決的問題
    • 使用高速緩存
  • 3 內存管理函數
    • kmalloc
    • kzalloc
    • vmalloc
    • vzalloc
    • 區別
  • 參考文章

內核使用struct page結構體描述每個物理頁,也叫頁框。內核在很多情況下,需要申請連續的頁框,而且數量不定,比如4個、5個、9個等。如果頻繁地請求和釋放不同大小的一組連續的頁框,必然導致在已分配的塊內分散了許多小塊的空閑頁面,由此帶來的問題是,即使有足夠的空閑頁框可以滿足請求,但要分配一個大塊的連續頁框可能無法滿足請求。為了避免這種情況,Linux內核引入了伙伴算法。

1 伙伴算法

伙伴算法把所有的空閑頁框分為11個塊鏈表,每塊鏈表中分布包含特定的連續頁框內存空間,在第i條鏈表中,每個鏈表元素包含2的i次方個連續頁框。
請添加圖片描述
假設要申請一個256個頁框的塊,先從256個頁框的鏈表中查找空閑塊,如果沒有,就去512個頁框的鏈表中找,找到了則將頁框塊分為2個256個頁框的塊,一個分配給應用,另外一個移到256個頁框的鏈表中。如果512個頁框的鏈表中仍沒有空閑塊,繼續向1024個頁框的鏈表查找,如果仍然沒有,則返回錯誤。頁框塊在釋放時,會主動將兩個連續的頁框塊合并為一個較大的頁框塊。
從上面可以知道Buddy算法一直在對頁框做拆開合并拆開合并的動作。Buddy算法牛逼就牛逼在運用了世界上任何正整數都可以由2^n的和組成。這也是Buddy算法管理空閑頁表的本質。

頁框操作

alloc_pages()

static inline struct page *
alloc_pages(unsigned int gfp_mask, unsigned int order);

該函數分配2的order次方個連續的頁框,并返回一個指針,該指針指向第一個頁page結構體,如果出錯,返回NULL。可以使用下面這個函數把給定的頁轉為它的邏輯地址:

void *page_address(struct page *page);

該函數返回一個指針,指向給定物理頁當前所在的邏輯地址。

其他頁框操作可以看這篇文章:https://blog.csdn.net/qq_41683305/article/details/123966721

2 slab

在Linux中,伙伴算法是以頁為單位管理和分配內存。但是現實的需求卻以字節為單位,假如我們需要申請20Bytes,總不能分配一頁吧!那此不是嚴重浪費內存。那么該如何分配呢?slab分配器就應運而生了,專為小內存分配而生。slab分配器分配內存以字節為單位。但是slab分配器并沒有脫離伙伴算法,而是基于伙伴算法分配的大內存進一步細分成小內存分配。
我們先來看一張圖:
請添加圖片描述

kmem_cache是cache_chain上的一個元素,kmem_cache描述了一個高速緩存,每個高速緩存包含了一個slabs的列表,這通常是一段連續的內存塊。存在3種slab:

  • slabs_full:slab都已經分配完
  • slabs_partial:slab部分分配
  • slab_empty:空slab或者沒有對象被分配。

kmem_cache高速緩存以緩存對象的大小來區分,所包含的三種slab都是鏈表,里面有一個或多個slab,每個slab由一個或多個連續的物理頁組成,在物理頁上保存的才是對象。

slab是slab分配器的最小單位,在實現上一個slab由一個或多個連續的物理頁組成(通常只有一頁)。單個slab可以在slab鏈表之間移動,例如如果一個半滿slab被分配了對象后變滿了,就要從slabs_partial中被刪除,同時插入到slabs_full中去。

slab機制要解決的問題

  1. 減少伙伴算法在分配小塊連續內存時所產生的內部碎片
  2. 將頻繁使用的對象緩存起來,減少分配、初始化和釋放對象的時間開銷
  3. 通過著色技術調整對象以更好的使用硬件高速緩存

使用高速緩存

一個新的高速緩存是通過以下函數創建的:

kmem_cache_t *kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags,void (*ctor)(void *, kmem_cache_t *, unsigned long),void (*dtor)(void *, kmem_cache_t *, unsigned long));

第一個參數是字符串,存放著高速緩存的名字。第二個參數是高速緩存中每個元素的大小,第三個參數就是高速緩存內第一個對象的偏移,這用來確保在頁內進行特定的對齊,通常情況,0就可以滿足要求,也就是標準對齊。flags是可選的設置項,用來控制高速緩存的行為。

我們使用cat /proc/slabinfo可以看到系統所有的高速緩存:
在這里插入圖片描述
創建高速緩存之后,就可以通過下列函數從中獲取對象:

void *kmem_cache_alloc(kmem_cache_t *cachep,int flags);

該函數從給定的高速緩存cachep中返回一個指向對象的指針。如果高速緩存的所有slab中都沒有空閑的對象,那么slab層必須通過kmem_getpages()獲取新的頁,flags的值傳遞給__get_free_pages()。

最后釋放一個對象,并把它返回給原先的slab,可以使用下面的函數:

void kmem_cache_free(kmem_cache_t *cachep,void *objp);

這樣就能把高速緩存cachep中的對象objp標記為空閑了。

要銷毀一個高速緩存,則調用:

int kmem_cache_destroy(kmem_cache_t *cachep);

同樣,也不能從中斷上下文中調用這個函數,因為它也可能會睡眠。調用該函數之前必須確保以下兩個條件:

  • 高速緩存中的所有slab都必須為空
  • 在調用kmem_cache_destroy()期間,不能再訪問這個高速緩存

3 內存管理函數

kmalloc

void *kmalloc(size_t size, int flags);
  • size:是指要分配的內存的字節數。
  • flags:是分配標志,它提供了多種kmalloc( )的行為

這個函數返回一個指向內存塊的指針,其內存塊至少要有size大小,所分配的內存區在物理上是連續的,在出錯時,它返回NULL,除非沒有足夠的內存可用,否則內核總能分配成功。在使用kmalloc()時,必須檢查返回值是不是NULL。

kzalloc

void *kzalloc(size_t size, int flags);

kzalloc的功能比kmalloc多了一步,會將申請到連續物理內存數據置為0

vmalloc

void *vmalloc(unsigned long size);

該函數返回一個指針,指向邏輯上連續的一塊內存區,其大小至少為size。在發生錯誤時,函數返回NULL。函數可能睡眠,因此,不能從中斷上下文中進行調用,也不能從其他不允許阻塞的情況下使用。

vzalloc

void *valloc(unsigned long size);

vzalloc比vmalloc步驟多了一步,將申請的邏輯地址連續的內存數據置為0。

區別

kmalloc和kzalloc申請的內存在物理上連續,vmalloc和vzalloc申請的內存在物理上不需要連續,它們在邏輯上連續。kmalloc和kzalloc申請的內存可由kfree函數釋放

void kfree(const void *ptr);

vmalloc和vzalloc申請的內存可由vfree函數釋放

void vfree(void *addr);

參考文章

https://zhuanlan.zhihu.com/p/36140017

https://www.cnblogs.com/cherishui/p/4246133.html

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

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

相關文章

eval 函數 代替函數_eval()函數以及JavaScript中的示例

eval 函數 代替函數eval()函數 (eval() function) eval() function is a predefined global function in JavaScript and it is used to evaluate (execute) an expression, which is passed to the function as a parameter. It can also evaluate any JavaScript code. eval(…

F# ≥ C#(活動模式 和枚舉)

F#提供了一個叫"活動模式"的有趣功能。它把輸入的數據轉換成其他不同的東西。 一個有趣的使用實例就是代替枚舉。但我編程枚舉的時候,我總不高興去鏈接枚舉項到它的定義。例如,下面的枚舉定義了 數字枚舉, enum Numbers{Odd,Even,}…

關于java的classpath設置

今天晚上實驗室的另一個人在編譯一個java程序,需要用到一個jar文件,所以在命令行編譯的時候需要添加jar的路徑,例如: java -classpath demo.jar hello 但是設置了path之后java就不會搜索當前目錄,也就是所如果hello在當…

C語言uthash的用法

文章目錄1 定義一個哈希表鍵值UT_hash_handle2 哈希操作聲明添加查找刪除獲取哈希表中元素個數迭代排序3 案例鍵的使用官網解釋:https://troydhanson.github.io/uthash/userguide.html 在使用之前,我們必須包含uthash.h的頭文件,你需要將該頭…

Javascript Paste Keyboard Shortcuts Hijack

author : kj021320 team : I.S.T.O 這樣的攻擊手段也算是極其無恥 猥瑣之極! 所以防御措施一定要做好 首先說一下通過Javascript Paste Keyboard Shortcuts Hijack能做什么???能夠讀取你本地機器任何文件! 沒錯!也就是說 你中了任何一個XSS 加上你按了粘貼快捷鍵后,你就有可…

python 生成器表達式_Python中的列表理解與生成器表達式

python 生成器表達式The list is a collection of different types of elements and there are many ways of creating a list in Python. 該列表是不同類型元素的集合,并且有許多方法可以在Python中創建列表。 清單理解 (List Comprehension) List comprehension…

Javaweb---監聽器

1.什么是監聽器 監聽器就是監聽某個對象的狀態變化的組件。 事件源:被監聽的對象 ----- 三個域對象 request session servletContext 監聽器:監聽事件源對象 事件源對象的狀態的變化都會觸發監聽器 ---- 62 注冊監聽器:將監聽器與事件源進行…

Linux中的Ramdisk和Initrd

Ramdisk簡介先簡單介紹一下ramdisk,Ramdisk是虛擬于RAM中的盤(Disk)。對于用戶來說,能把RAM disk和通常的硬盤分區(如/dev/hda1)同等對待來使用,例如:redice # mkfs.ext2 /dev/ram0mke2fs 1.38 (30-Jun-200…

slab下kmalloc內核函數實現

文章目錄kmalloc的整體實現獲取高速緩存高速緩存獲取index總結https://blog.csdn.net/qq_41683305/article/details/124554490,在這篇文章中,我們介紹了伙伴算法、slab機制和常見的內存管理函數,接下來,我們看看kmalloc內核函數的…

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() …

標題:三羊獻瑞

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

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…