RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux的內(nèi)存管理是什么,Linux的內(nèi)存管理詳解

Linux內(nèi)核補(bǔ)給站 ? 來源:Linux內(nèi)核補(bǔ)給站 ? 作者:Linux內(nèi)核補(bǔ)給站 ? 2022-05-11 17:54 ? 次閱讀

Linux的內(nèi)存管理

Linux的內(nèi)存管理是一個非常復(fù)雜的過程,主要分成兩個大的部分:內(nèi)核的內(nèi)存管理和進(jìn)程虛擬內(nèi)存。內(nèi)核的內(nèi)存管理是Linux內(nèi)存管理的核心,所以我們先對內(nèi)核的內(nèi)存管理進(jìn)行簡介。

一、物理內(nèi)存模型


pYYBAGJ7h8qAfJe-AAA3U8e_SDc509.jpg?source=d16d100b

?

物理內(nèi)存模型主要分為兩種:UMA(Uniform Memory Access)和NUMA(Non-Uniform Memory Access)。

UMA模型是指物理內(nèi)存是連續(xù)的,SMP系統(tǒng)中的每個處理器訪問各個內(nèi)存區(qū)都是同樣快的;而NUMA模型則是指SMP中的每個CPU都有自己的物理內(nèi)存區(qū),雖然CPU可以訪問其他CPU的內(nèi)存區(qū),但是要比方位自己的內(nèi)存區(qū)慢得多。我們一般使用的物理模型都是UMA模型。為了NUMA模型,Linux提供了三種可能的內(nèi)存布局配置:Flat Memory, Sparse Memory, Discontiguous Memory。


poYBAGJ7h8qAFB54AACVOF1bFvY111.jpg?source=d16d100b

?

Flat Memory就是簡單的線性組織物理內(nèi)存,一般沒有內(nèi)存空洞的UMA架構(gòu)都采用這種配置。對于NUMA模型,一般只能采用后兩者,而后兩者的區(qū)別主要在于:Sparse Memory配置一般認(rèn)為是試驗(yàn)性的,不是那么穩(wěn)定,但是有一些新的功能和性能優(yōu)化,而Discontiguous Memory配置一般認(rèn)為是穩(wěn)定的,但不具有內(nèi)存熱插拔之類的新特性。

二、物理內(nèi)存組織

  • 物理內(nèi)存的組織主要分為兩個部分:節(jié)點(diǎn)(node)和內(nèi)存與內(nèi)存域(zone)。
  • node主要針對NUMA設(shè)計(jì),在NUMA的SMP系統(tǒng)中,每個處理器都有一個自己的node,而在UMA模型中則只有一個node。對于每個node中的內(nèi)存,Linux分成了若干內(nèi)存域,定義在mmzone.h的zone_type中,常用的有ZONE_DMA、ZONE_DMA32、ZONE_NORMAL、ZONE_HIGHMEM和ZONE_MOVABLE。其中ZONE_NORMAL是最為常用的,表示內(nèi)核能夠直接映射的一般內(nèi)存區(qū)域;ZONE_DMA表示DMA內(nèi)存區(qū);ZONE_DMA32表示64位系統(tǒng)中對于32位DMA設(shè)備使用的內(nèi)存;ZONE_HIGHMEM表示在32位系統(tǒng)中,高地址內(nèi)存的區(qū)域;ZONE_MOVABLE與伙伴系統(tǒng)的內(nèi)存碎片消除有關(guān)。后文會詳細(xì)介紹相關(guān)部分。
  • 在物理內(nèi)存管理過程中有一些名詞:
  1. Page Frame(頁幀,或稱頁框):是系統(tǒng)內(nèi)存管理的最小單位,系統(tǒng)中每個頁框都是struct page的一個實(shí)例。IA-32系統(tǒng)的頁框大小是4KB。
  2. Hot-n-Code Pages(冷熱頁):是指內(nèi)存管理中對頁框的分類,訪問較多的或者近期訪問的為熱頁,否則為冷頁。該標(biāo)記主要與內(nèi)存換出(memory swap)相關(guān)。
  3. Page Table(頁表):是內(nèi)存尋址過程中的輔助數(shù)據(jù)結(jié)構(gòu)。層次化的頁表對于大地之空間的快速、高效管理很有意義。Linux一般支持四級頁表:PGD(Page Global Directory)、PUD(Page Upper Directory)、PMD(Page Middle Directory)和PTE(Page Table Entry)。IA-32體系中默認(rèn)只是用了兩級分頁系統(tǒng),即只有PGD和PTE。

三、X86架構(gòu)下的內(nèi)存布局

內(nèi)核在內(nèi)存中的布局

Linux的內(nèi)核在初始化的時候會被加載到內(nèi)存區(qū)的固定位置(在此我們不討論可重定位內(nèi)核的情況),而內(nèi)核所占用的內(nèi)存區(qū)域的布局是固定的,如圖:


pYYBAGJ7h8qAAVYKAACGj-7Qi5s581.jpg?source=d16d100b

?

內(nèi)存第一個頁框不實(shí)用,主要被BIOS用來初始化;之后的連續(xù)640KB內(nèi)存也不被內(nèi)核使用,主要用來映射各種ROM(通常是BIOS和顯卡ROM);再之后的空間是閑置的,原因是內(nèi)核要被放在連續(xù)的內(nèi)存空間。在0x100000開始為內(nèi)核部分,分別是代碼段、數(shù)據(jù)段和附加段。

IA-32架構(gòu)的布局

IA-32架構(gòu)可以訪問4GB的地址空間(不考慮PAE),常規(guī)情況下會將4GB線性空間劃分成3:1的兩部分:低地址的3/4部分為用戶空間,而高地址的1GB是內(nèi)核空間,即內(nèi)核地址空間從偏移量0xC0000000開始,每個虛擬地址x都對應(yīng)于物理地址x-0xC0000000。這樣的設(shè)計(jì)加快了內(nèi)核空間尋址的速度(簡單的減法操作)。在進(jìn)程切換的過程中,只有用戶空間的低3GB內(nèi)存對應(yīng)的頁表會被切換,高地址空間會公用內(nèi)核頁表。

IA-32架構(gòu)的這種設(shè)計(jì)存在著一個問題:既然內(nèi)核只能處理1GB的空間(事實(shí)上,內(nèi)核處理的空間還不足1GB,后面會詳細(xì)說明),那么如果物理內(nèi)存大于1GB,剩下的內(nèi)存將如何處理?這種情況下,內(nèi)核將無法直接映射全部物理內(nèi)存,這樣就用到了上面所說的高地址內(nèi)存域(ZONE_HIGHMEM)。具體的內(nèi)存分配如下圖:


poYBAGJ7h8qAeuIIAACB3dFVSlQ318.jpg?source=d16d100b

?

能夠看到內(nèi)核區(qū)域的映射從__PAGE_OFFSET(0xC00000)開始,即3GiB位置開始映射到4GiB,開始的一段用來直接映射,而后面有128MB的VMALLOC空間(這部分空間的使用后文將講到),再之后有永久映射和固定映射的空間(從PKMAP_BASE開始)。所以事實(shí)上物理內(nèi)存能夠直接映射的空間為1GB-VMALLOC-固定映射-永久映射,所以真正大約只有850MB多一點(diǎn),也就是說,物理內(nèi)存中只有前850多MB是可以直接映射到內(nèi)核空間的,對于超過的部分來說,將作為高地址空間(HIGHMEM)。高地址空間可以在VMALLOC、永久映射和固定映射部分使用到。

到這里可能會有這樣一個疑問:如果內(nèi)核只能處理896MB的空間,那么如果內(nèi)存很大(比如3GB),剩下的空間的利用率和利用效率豈不是很低?對于這個問題我們需要注意:這里我們所講述的:1、這里的內(nèi)存都是內(nèi)核在內(nèi)核區(qū)的1GB空間里對物理內(nèi)存的訪問,用戶對物理內(nèi)存的訪問不是通過直接映射來訪問的,還有另外一套機(jī)制;2、這里的內(nèi)存僅僅是通過直接映射得到的內(nèi)存,內(nèi)核還可以通過其他的方式訪問到較高地址的內(nèi)存。

還有一個普遍的疑問就是:內(nèi)核直接映射占用了800多MB的空間,那么如果我們又3GB的物理內(nèi)存,是不是只有2GB多一點(diǎn)的實(shí)際可用內(nèi)存呢?這個說法是錯誤的,上圖所描述的只是內(nèi)核在線性地址空間的分布情況,其中的任何區(qū)域如果沒有真正的物理內(nèi)存與之映射的話是不會真正占用物理內(nèi)存的,而物理內(nèi)存在分配的過程中(用戶申請內(nèi)存、VMALLOC部分等),更傾向于先分配高地址內(nèi)存,在高地址內(nèi)存耗盡的情況下才會使用低850MB內(nèi)存。

AMD64架構(gòu)的布局

AMD64架構(gòu)采用了與IA-32完全不同的布局模式。由于64位的尋址空間的64位長,而在真正的實(shí)現(xiàn)過程中64位長尋址會造成較大的開銷,所以Linux目前僅適用了48位長的地址空間,但是為了向后兼容仍然適用64位地址空間表示。在布局方面考慮,如果單純采用48位類似IA-32的布局方式的話,則很難保證向后兼容性。所以AMD64架構(gòu)下的內(nèi)存布局Linux采用了一種特殊的方式,如圖:


pYYBAGJ7h8qAHyp0AACoBAEKKxU508.jpg?source=d16d100b

?

Linux將內(nèi)存分成了高地址部分和低地址部分兩部分,即下半部空間0~0x0000 7FFF FFFF FFFF和上半部空間0xFFFF 8000 0000 0000~0xFFFF FFFF FFFF FFFF。可以看到虛擬地址的低47位,即[0,46]為有效位,[47,63]的值總是相同的:或者全為0或者全為1。除此之外的值都是無效的。這樣在虛擬內(nèi)存空間中就將內(nèi)存分成了兩個部分:內(nèi)存空間的下半部和上半部。下半部為用戶空間,上半部為內(nèi)核空間。我們考慮內(nèi)核空間部分,下半部的前MAXMEM大?。?4TB)為直接映射地址,之后有一個空洞,主要目的是處理內(nèi)存訪問越界;再之后是大小為32TB的VMALLOC空間,在之后是VMMEMMAP空間、KERNEL TEXT段空間以及Modules空間。

在這里我們不仔細(xì)講述AMD64架構(gòu)的布局,以后的部分則主要關(guān)注于IA-32架構(gòu)。

四、啟動過程期間的內(nèi)存管理

在啟動過程中,盡管內(nèi)存管理尚未初始化,但內(nèi)核仍然需要分配內(nèi)存以創(chuàng)建各種數(shù)據(jù)結(jié)構(gòu)。bootmem分配器用于在啟動階段早期分配內(nèi)存。由于對這部分內(nèi)存分配集中于簡單性方面而不是性能和通用性,因此使用的是最先適配(first-fit)分配器。該分配器使用一個位圖來管理頁,位圖中的1表示頁已使用,0表示未使用。在需要分配內(nèi)存時,分配器掃描位圖,直到找到一個能夠提供足夠連續(xù)頁的為之,即最先最佳(first-best)或最先適配位置。

在這個分配過程中,需要處理一些不可分配的頁面,如IA-32系統(tǒng)中的0頁。另外對于IA-32系統(tǒng),bootmem僅僅使用了低地址部分,對于高地址部分的操作過于麻煩,所以在這里被放棄了。

在這個部分有一個很有意思的事情。我們在編寫內(nèi)核模塊的時候,對于模塊的初始化函數(shù)會使用__init標(biāo)記或者_(dá)_init_data標(biāo)記。對于被這兩個關(guān)鍵字標(biāo)記的函數(shù)和數(shù)據(jù),是只有在初始化階段才用到的,在bootmem退出的時候會全部被回收。而這部分代碼和數(shù)據(jù)再內(nèi)核鏈接的過程中將會被放在.init.text段和.init.data段,并統(tǒng)一放在內(nèi)核的尾部,在啟動結(jié)束后便于回收。

五、物理內(nèi)存的管理

1、伙伴系統(tǒng)

物理內(nèi)存管理中伙伴系統(tǒng)是最為重要的一個系統(tǒng),伙伴系統(tǒng)也基于一種相對簡單然而令人吃驚的強(qiáng)大算法,到目前已經(jīng)使用了幾乎40年?;锇橄到y(tǒng)在這里不再贅述,簡單谷歌一下就可以查到該算法的描述(實(shí)在是很簡單)。在這里主要講一下Linux Kernel的伙伴系統(tǒng)以及在2.6.24之后版本的系統(tǒng)中對伙伴系統(tǒng)的改良。

在上文中已經(jīng)說到,物理內(nèi)存的慣例分為若干個node,每個node中又有若干個zone。對于每個zone,都會有對應(yīng)的伙伴系統(tǒng),如下圖所示:


poYBAGJ7h8qAByEoAABTUqTH21c816.jpg?source=d16d100b

?

上圖中的Fallback list指的是:在多個node的系統(tǒng)中,如果某個node的內(nèi)存空間不夠,則會在Fallback List中指定的node中分配內(nèi)存。

我們可以執(zhí)行cat /proc/buddyinfo,能夠看到大約如下所示的信息

/proc/buddyinfo:

wolfgang@meitner> cat /proc/buddyinfo

Node 0, zone DMA 3 5 7 4 6 3 3 3 1 1 1

Node 0, zone DMA32 130 546 695 271 107 38 2 2 1 4 479

Node 0, zone Normal 23 6 6 8 1 4 3 0 0 0 0

顯示的三個域則是我們使用到的內(nèi)存域。

伙伴系統(tǒng)會出現(xiàn)一個很常見的問題:在系統(tǒng)使用較長時間之后,內(nèi)存中經(jīng)常出現(xiàn)較多碎片。對于這種情況,內(nèi)核將內(nèi)存頁面分成五種類型:

MIGRATE_UNMOVABLE

MIGRATE_RECLAIMABLE

MIGRATE_RESERVE

MIGRATE_MOVABLE

MIGRATE_ISOLATE

其中MIGRATE_RESERVE所表示的內(nèi)存是被系統(tǒng)保留以備急用的;MIGRATE_UNMOVABLE是不可移動的,如BIOS信息頁;MIGRATE_RECLAIMABLE在swap系統(tǒng)中使用;MIGRATE_ISOLATE表示不能從這里分配的內(nèi)存;MIGRATE_MOVABLE表示可以移動的內(nèi)存。對于內(nèi)核來說,MIGRATE_MOVABLE部分的內(nèi)存可以采用某種算法來進(jìn)行移動,使得內(nèi)存中的碎片減少。另外內(nèi)核還維護(hù)了一個fallback list,來表示如果在某個類型中分配頁面未成功,會在哪些類型的頁面中來分配。

具體的信息可以在/proc/pagetypeinfo中看到

2、伙伴系統(tǒng)的內(nèi)存分配API

基本上從如下兩張圖就能夠看出來:


pYYBAGJ7h8qAREeSAABYcMJDLT4703.jpg?source=d16d100b

?


poYBAGJ7h8qARHxTAABAYlzSAbI033.jpg?source=d16d100b

?

對于其中的函數(shù)命名基本都是自明的,主要的差別在于:對于雙下劃線開頭的函數(shù)(__get_free_page, __free_page等)返回值或者參數(shù)為struct page *,而其他的函數(shù)返回值為unsigned long,即線性地址地址。

3、內(nèi)核中不連續(xù)頁的分配

根據(jù)上文的講述,我們知道物理上連續(xù)的映射對內(nèi)核是最好的,但并不是總能成功的使用。所以內(nèi)核提供了類似用戶空間訪問內(nèi)存一樣的機(jī)制(vmalloc)來進(jìn)行對內(nèi)核中不連續(xù)頁的分配。這一部分就是上文中所說的vmalloc區(qū)域。這部分主要是一個vmalloc函數(shù):

 void *vmalloc(unsigned long size);

在該函數(shù)的實(shí)現(xiàn)過程中,需要先申請一部分虛擬內(nèi)存空間vm_area,然后將這部分空間映射到vmalloc區(qū)域中。對于映射的物理內(nèi)存,內(nèi)核更傾向于使用高地址空間(ZONE_HIGHMEM),來節(jié)省寶貴的地地址空間。對于不同vmalloc調(diào)用申請的vm_area之間,會有一個hole來隔離,以避免越界訪問。


pYYBAGJ7h8uAZfKQAABjVlY6VMI002.jpg?source=d16d100b

?

注意vmalloc系統(tǒng)底層也是使用伙伴系統(tǒng)來分配內(nèi)存,所以申請內(nèi)存的大小只能是整頁的(頁大小對齊)。

在這部分有一個有意思的事情:vmalloc區(qū)域在IA-32中預(yù)設(shè)的大小是128MB,這部分內(nèi)存一般會被內(nèi)核模塊使用。vmalloc區(qū)域的大小是可以定制的,在新版內(nèi)核中可以在內(nèi)核啟動選項(xiàng)中加入vmalloc=xxxMB的方式來修改,或者修改內(nèi)核代碼對應(yīng)的宏:

unsigned int __VMALLOC_RESERVE = 128 << 20;

如果修改了vmalloc區(qū)域的大小,那么內(nèi)核能夠直接映射的區(qū)域?qū)s小,即kmalloc能夠使用的內(nèi)存將會變少(kmalloc使用slab allocator分配,后文將會介紹),但是內(nèi)核真正使用的物理內(nèi)存和vmalloc區(qū)域的大小沒有直接關(guān)系。所以在內(nèi)核模塊的編寫過程中,要根據(jù)需求來使用vmalloc和kmalloc,而了解他們的內(nèi)存分配機(jī)制是很有好處的。

4、內(nèi)核映射

盡管vmalloc函數(shù)族可用于從高端內(nèi)存向內(nèi)核映射頁框,但這并不是這些函數(shù)的實(shí)際用途。內(nèi)核提供了其他函數(shù)用于將ZONE_HIGHMEM頁框顯式的映射到內(nèi)核空間。

如果需要長期的將高端頁框映射到內(nèi)核地址空間中,即持久映射,需要使用kmap函數(shù),映射的空間指向上文圖中所指Persistent Mapings。內(nèi)核使用kunmap接觸映射。持久映射kmap函數(shù)不能用于處理中斷處理程序,因?yàn)閗map過程可能進(jìn)入睡眠狀態(tài)。

為了能夠原子的執(zhí)行映射過程(邏輯上稱為kmap_atomic),內(nèi)核提供了臨時映射機(jī)制,也被稱作固定映射,頁面也會被映射到Fixmaps區(qū)域。映射的API分別是kmap_atomic和kunmap_atomic。固定映射可以用在中斷處理程序中。

對于不支持高端內(nèi)存的體系結(jié)構(gòu)(如64位體系結(jié)構(gòu)),則將以上若干映射函數(shù)通過預(yù)編譯選項(xiàng)指向了對應(yīng)的兼容函數(shù)。事實(shí)上對于這些體系結(jié)構(gòu)的映射,都是簡單的返回對應(yīng)的內(nèi)存地址即可,因?yàn)閮?nèi)核可以在直接映射區(qū)域簡單的找到對應(yīng)的地址。

六、slab分配器

上面所描述的物理內(nèi)存管理機(jī)制中,最小粒度的內(nèi)存管理單元是頁框,大小一般是4KB,而在內(nèi)存中無論何時申請內(nèi)存都分配一個頁面是不合適的方式,所以引入了新的管理機(jī)制,即slab分配器。Slab是Sun公司的一個雇員Jeff Bonwick在Solaris 2.4中設(shè)計(jì)并實(shí)現(xiàn)的。slab分配器將大小相同的內(nèi)核對象放在一起,當(dāng)對象被free了之后并不是直接還給伙伴系統(tǒng),而是將這部分對象的頁面保存下來,在下一次該類對象的內(nèi)存申請時分配給新的對象。這種機(jī)制的優(yōu)勢在于:1、能夠按照CPU緩存的大小來組織分配對象的位置,一般來說,都會將若干個相同的對象放在一個cacheline中,并且對象占用的內(nèi)存不會跨越兩個cacheline。這樣的設(shè)計(jì)能夠保證slab分配器分配的對象能夠較多時間的存在于CPU緩存中。2、采用LIFO方式管理對象。這種做法基于:最近釋放的對象空間是最有可能存在于cache中的。這也能夠有效的利用cache。

各個緩存管理的對象,會合并為較大的組,覆蓋一個或者多個連續(xù)的頁框。這種組稱作slab,每個緩存由幾個這種slab組成。這也是slab分配器命名的由來。

1、slab、slob、slub分配器

Linux內(nèi)核中目前支持三種分配器,其中slab前文已經(jīng)簡單介紹過了,另外兩種分配器是備選分配器,可以在內(nèi)核編譯選項(xiàng)中指定。由于對上層提供的API是固定的,僅僅是底層實(shí)現(xiàn)不同,所以Kernel開發(fā)者不必去考慮底層的分配情況。

slab分配器雖然有很大的優(yōu)勢,但是其存在兩個問題:1、在較小的內(nèi)存系統(tǒng)下,slab分配器過于復(fù)雜。如嵌入式環(huán)境下slab顯得有些過于龐大。2、在內(nèi)存很大的巨型機(jī)上,slab分配器本身的數(shù)據(jù)結(jié)構(gòu)所占用的內(nèi)存空間過大,最大的可高達(dá)2GB以上。

對于前一種情況,設(shè)計(jì)了slob分配器,它圍繞一個simple linked list of block展開(也是slob的由來),在分配內(nèi)存的時候,采用了最先適配算法(first-fit)。

對于后一種情況,設(shè)計(jì)了slub分配器,slub分配器將頁框打包為組,并通過struct page中未使用的字段來管理這些組,試圖最小化內(nèi)存開銷。slub分配器事實(shí)上是基于slab分配器的一種優(yōu)化結(jié)構(gòu)。在大型機(jī)上slub分配器上有著更好的性能。

2、slab分配器的原理

內(nèi)核中一般的內(nèi)存分配和釋放的函數(shù)有kmalloc、kzalloc、kcalloc。這三個函數(shù)的區(qū)別是:kmalloc僅僅申請一片空間,kzalloc在申請一篇空間之后將其置0。kcalloc很少用,即對數(shù)組進(jìn)行空間分配并置0。

所有活動的slab緩存可以通過cat /proc/slabinfo來看到。

slab分配器由一個緊密交織的數(shù)據(jù)和內(nèi)存結(jié)構(gòu)的網(wǎng)絡(luò)組成,主要可以分為如圖的兩部分:保存管理型數(shù)據(jù)的緩存對象和保存被管理對象的各個slab。


poYBAGJ7h8uAK6uAAAA0zFW6GJI616.jpg?source=d16d100b

?

每個slab緩存只負(fù)責(zé)一種對象類型,或者提供一般性的緩沖區(qū)。下圖中給出了緩存的精細(xì)結(jié)構(gòu):


pYYBAGJ7h8uAZCu1AABizruwUwI393.jpg?source=d16d100b

?

可以看到對于每個slab緩存,都會保存成一個struct kmem_cache,每個結(jié)構(gòu)里面包含一個穿在一起的鏈表,以及三個鏈表頭:free、partial、full,分別表示空閑鏈、部分空閑鏈和滿鏈。含義和字面意思相同。對象在slab中并不是連續(xù)排列的,用戶可以要求對象按硬件緩存對齊,也可以要求按照BYTES_PER_WORD對齊,該值表示void指針?biāo)璧淖止?jié)的數(shù)目。

創(chuàng)建新的slab緩存需要調(diào)用kmem_cache_create函數(shù),返回struct kmem_cache結(jié)構(gòu)。創(chuàng)建緩存的時候需要制定緩存的可讀name(會出現(xiàn)在/proc/slabinfo中),還需要制定被管理對象以字節(jié)計(jì)的長度(size),在對齊數(shù)據(jù)時使用的偏移量(align),以及flags標(biāo)志。另外還需要制定構(gòu)造/析構(gòu)函數(shù)ctor/dtor。

分配對象時調(diào)用kmem_cache_alloc,函數(shù)需要指定創(chuàng)建過的slab緩存,以及flags。內(nèi)核支持的所有GFP_xxx宏都可以用于指定標(biāo)志。

下圖顯示了分配對象的過程:


poYBAGJ7h8uAKzrvAAB-dHvi8iI358.jpg?source=d16d100b

?

3、通用緩存

如果不涉及對象緩存,而是傳統(tǒng)意義上的分配/釋放內(nèi)存,則需要調(diào)用kmalloc和kfree函數(shù),而這兩個函數(shù)的后端依然是使用slab分配器進(jìn)行分配的。kmalloc的基礎(chǔ)是一個數(shù)組,其中是一些分別用于不同內(nèi)存長度的slab緩存,數(shù)組項(xiàng)是cache_sizes的實(shí)例。該數(shù)據(jù)結(jié)構(gòu)定義如下:


 
struct cache_sizes {
    size_t cs_size;
    kmem_cache_t *cs_cachep;
    kmem_cache_t *cs_dmacachep;
#ifdef CONFIG_ZONE_DMA
    struct kmem_cache *cs_dmacachep;
#endif
}

cs_size指定了該項(xiàng)負(fù)責(zé)的內(nèi)存區(qū)的長度。每個長度對應(yīng)兩個slab緩存,其中之一提供適合DMA訪問的內(nèi)存。通過cat /proc/slabinfo中能夠看到,kmalloc-xxx和dma-kmalloc-xxx就是這一部分提供的。

kmalloc定義在,該函數(shù)首先檢查所需的緩存是否用常數(shù)來指定,如果是這種情況,所需的緩存可以在編譯時靜態(tài)確定,這可以提高速度(內(nèi)核的優(yōu)化真是無所不用其極?。。?。否則,該函數(shù)調(diào)用__kmalloc查找長度匹配的緩存,后者是__do_kmalloc的前端,提供參數(shù)轉(zhuǎn)換功能。

mm/slab.c
 
void *__do_kmalloc(size_t size, gfp_t flags)
{
    kmem_cache_t *cachep;
    cachep = __find_general_cachep(size, flags);
    if (unlikely(ZERO_OR_NULL_PTR(cachep)))
        return NULL;
    return __cache_alloc(cachep, flags);
}

__find_general_cachep在上文提到的緩存中找到適當(dāng)?shù)囊粋€,之后使用__cache_alloc函數(shù)完成最終的分配。

七、處理器高速緩存和TLB控制

在這里簡單的總結(jié)一些Kernel中和TLB/高速緩存相關(guān)的函數(shù),具體TLB的實(shí)現(xiàn)機(jī)制與體系架構(gòu)相關(guān)性很大,就不詳細(xì)總結(jié)了。

flush_tlb_all和flush_cache_all刷出整個TLB/高速緩存。此操作只在操縱內(nèi)核頁表時需要,因?yàn)榇祟愋薷牟粌H影響所有進(jìn)程,而且影響系統(tǒng)中的所有處理器。

flush_tlb_mm(struct mm_struct *mm)和flush_cache_mm刷出所有屬于地址空間mm的TLB/高速緩存項(xiàng)。

flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)和flush_cache_range(vma, start, end)刷出地址范圍vma->vm_mm中虛擬地址start和end之間的所有TLB/高速緩存項(xiàng)。

flush_tlb_page(struct vm_area_struct *vma, unsigned long page) 和flush_cache_page(vma, page)刷出虛擬地址在[page, page + PAGE_SIZE]范圍內(nèi)所有的TLB/高速緩存項(xiàng)。

update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte)在處理頁失效之后調(diào)用。它在處理器的內(nèi)存管理單元MMU中加入信息,是的虛擬地址address由頁表項(xiàng)pte描述。僅當(dāng)存在外部MMU時才需要該函數(shù),通常MMU集成在處理器內(nèi)部。

此外,flush_cache_和flush_tlb_函數(shù)常常成對出現(xiàn),例如,在使用fork進(jìn)程復(fù)制進(jìn)程的地址空間時,則:1、刷出高速緩存,2、操作內(nèi)存,3、刷出TLB。這個順序很重要,因?yàn)?/p>

如果順序相反,那么在TLB刷出之后,正確信息提供之前,多處理器系統(tǒng)中的另一個CPU可能從進(jìn)程的頁表項(xiàng)取得錯誤的信息。

在刷出高速緩存時,某些體系結(jié)構(gòu)需要依賴TLB中的“虛擬->物理”轉(zhuǎn)換規(guī)則。flush_tlb_mm必須在flush_cache_mm之后執(zhí)行以確保這一點(diǎn)。

小結(jié)

這部分東西實(shí)在是太多,簡單的總結(jié)一下就已經(jīng)這么多了。在這里對以上的內(nèi)容進(jìn)行一個簡單的概括。

在內(nèi)核進(jìn)入正常運(yùn)行之后,內(nèi)存管理分為兩個層次:伙伴系統(tǒng)負(fù)責(zé)物理頁框的管理。在伙伴系統(tǒng)之上,所有的內(nèi)存管理都基于此,主要分為:slab分配器處理小塊內(nèi)存;vmalloc模塊為不連續(xù)物理頁框提供映射;永久映射區(qū)域和固定映射區(qū)域提供對高地址物理頁框的訪問。

內(nèi)存管理的初始化很具有挑戰(zhàn)性,內(nèi)核通過引入一個非常簡單的自舉內(nèi)存分配器(bootmem)解決了該問題,該分配器在正式的分配機(jī)制(伙伴系統(tǒng))啟用后停用。

審核編輯:符乾江

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11292

    瀏覽量

    209323
  • 文件系統(tǒng)
    +關(guān)注

    關(guān)注

    0

    文章

    284

    瀏覽量

    19904
  • 內(nèi)存管理
    +關(guān)注

    關(guān)注

    0

    文章

    168

    瀏覽量

    14134
  • 虛擬內(nèi)存
    +關(guān)注

    關(guān)注

    0

    文章

    77

    瀏覽量

    8058
收藏 人收藏

    評論

    相關(guān)推薦

    Linux kernel內(nèi)存管理模塊結(jié)構(gòu)分析

    基于上面章節(jié)的需求,Linux kernel從虛擬內(nèi)存(VM)、DMA mapping以及DMA buffer sharing三個角度,對內(nèi)存進(jìn)行管理.
    發(fā)表于 09-19 11:55 ?1770次閱讀
    <b class='flag-5'>Linux</b> kernel<b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>模塊結(jié)構(gòu)分析

    深度解析Linux內(nèi)存管理體系

    Linux內(nèi)存管理的整體模式是虛擬內(nèi)存管理(分頁內(nèi)存管理
    發(fā)表于 08-06 16:55 ?1733次閱讀

    走進(jìn)Linux內(nèi)存系統(tǒng)探尋內(nèi)存管理的機(jī)制和奧秘

    Linux 內(nèi)存是后臺開發(fā)人員,需要深入了解的計(jì)算機(jī)資源。合理的使用內(nèi)存,有助于提升機(jī)器的性能和穩(wěn)定性。本文主要介紹Linux 內(nèi)存組織結(jié)構(gòu)
    的頭像 發(fā)表于 01-05 09:47 ?1622次閱讀

    關(guān)于Linux內(nèi)存管理的詳細(xì)介紹

    Linux內(nèi)存管理是指對系統(tǒng)內(nèi)存的分配、釋放、映射、管理、交換、壓縮等一系列操作的管理。在
    發(fā)表于 03-06 09:28 ?1064次閱讀

    Linux內(nèi)核的內(nèi)存管理詳解

    內(nèi)存管理的主要工作就是對物理內(nèi)存進(jìn)行組織,然后對物理內(nèi)存的分配和回收。但是Linux引入了虛擬地址的概念。
    發(fā)表于 08-31 14:46 ?779次閱讀
    <b class='flag-5'>Linux</b>內(nèi)核的<b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b><b class='flag-5'>詳解</b>

    Linux內(nèi)核內(nèi)存管理架構(gòu)解析

    內(nèi)存管理子系統(tǒng)可能是linux內(nèi)核中最為復(fù)雜的一個子系統(tǒng),其支持的功能需求眾多,如頁面映射、頁面分配、頁面回收、頁面交換、冷熱頁面、緊急頁面、頁面碎片管理、頁面緩存、頁面統(tǒng)計(jì)等,而且對
    的頭像 發(fā)表于 01-04 09:24 ?653次閱讀
    <b class='flag-5'>Linux</b>內(nèi)核<b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>架構(gòu)解析

    linux內(nèi)存管理

    公交,地鐵,睡前必備,方便大家查閱,持續(xù)更新,敬請期待!---更新于2020-02-12linux 內(nèi)存管理Linux內(nèi)存初始化CPU是
    發(fā)表于 07-22 08:41

    linux內(nèi)存管理機(jī)制淺析

    本內(nèi)容介紹了arm linux內(nèi)存管理機(jī)制,詳細(xì)說明了linux內(nèi)核內(nèi)存管理,
    發(fā)表于 12-19 14:09 ?73次下載
    <b class='flag-5'>linux</b><b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>機(jī)制淺析

    linux內(nèi)存管理

    linux內(nèi)存管理
    發(fā)表于 10-24 11:12 ?3次下載
    <b class='flag-5'>linux</b><b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>

    你知道linux內(nèi)存管理基礎(chǔ)及方法?

    linux內(nèi)存管理采取的分頁存取機(jī)制,會將內(nèi)存中不經(jīng)常使用的數(shù)據(jù)塊交換到虛擬內(nèi)存中。linux
    發(fā)表于 04-28 17:12 ?1155次閱讀

    適當(dāng)了解Linux內(nèi)存管理等問題

    linux內(nèi)存管理還是比較復(fù)雜的,其中牽扯到很多方面的知識,這篇小博文算是自己對于內(nèi)存管理的一點(diǎn)點(diǎn)的總結(jié)
    發(fā)表于 05-13 10:10 ?567次閱讀
    適當(dāng)了解<b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>等問題

    嵌入式 Linux 中的內(nèi)存管理

    點(diǎn)擊 嵌入式 Linux 中的內(nèi)存管理
    發(fā)表于 11-02 10:36 ?12次下載
    嵌入式 <b class='flag-5'>Linux</b> 中的<b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>

    Linux內(nèi)存管理的基礎(chǔ)知識科普

    Linux內(nèi)存管理可謂是學(xué)好Linux的必經(jīng)之路,也是Linux的關(guān)鍵知識點(diǎn),有人說打通了內(nèi)存
    的頭像 發(fā)表于 06-08 15:24 ?2107次閱讀

    Linux內(nèi)存管理體系介紹

    內(nèi)存是計(jì)算機(jī)最重要的資源之一,內(nèi)存管理是操作系統(tǒng)最重要的任務(wù)之一。內(nèi)存管理并不是簡單地管理一下
    的頭像 發(fā)表于 08-08 09:28 ?1620次閱讀

    Linux 內(nèi)存管理總結(jié)

    一、Linux內(nèi)存管理概述 Linux內(nèi)存管理是指對系統(tǒng)內(nèi)存
    的頭像 發(fā)表于 11-10 14:58 ?523次閱讀
    <b class='flag-5'>Linux</b> <b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>總結(jié)
    RM新时代网站-首页