最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

一文給你解決linux內(nèi)存源碼分析- SLUB分配器概述(超詳細(xì))

2022-02-22 14:03 作者:補(bǔ)給站Linux內(nèi)核  | 我要投稿

SLUB和SLAB的區(qū)別

  • 首先為什么要說slub分配器,內(nèi)核里小內(nèi)存分配一共有三種,SLAB/SLUB/SLOB,slub分配器是slab分配器的進(jìn)化版,而slob是一種精簡(jiǎn)的小內(nèi)存分配算法,主要用于嵌入式系統(tǒng)。慢慢的slab分配器或許會(huì)被slub取代,所以對(duì)slub的了解是十分有必要的。

  • 我們先說說slab分配器的弊端,我們知道slab分配器中每個(gè)node結(jié)點(diǎn)有三個(gè)鏈表,分別是空閑slab鏈表,部分空slab鏈表,已滿slab鏈表,這三個(gè)鏈表中維護(hù)著對(duì)應(yīng)的slab緩沖區(qū)。我們也知道slab緩沖區(qū)的內(nèi)存是從伙伴系統(tǒng)中申請(qǐng)過來的,我們?cè)O(shè)想一個(gè)情景,如果沒有內(nèi)存回收機(jī)制的情況下,只要申請(qǐng)的slab緩沖區(qū)就會(huì)存入這三個(gè)鏈表中,并不會(huì)返回到伙伴系統(tǒng)里,如果這個(gè)類型的SLAB迎來了一個(gè)分配高峰期,將會(huì)從伙伴系統(tǒng)中獲取很多頁(yè)面去生成許多slab緩沖區(qū),之后這些slab緩沖區(qū)并不會(huì)自動(dòng)返回到伙伴系統(tǒng)中,而是會(huì)添加到node結(jié)點(diǎn)的這三個(gè)slab鏈表中去,這樣就會(huì)有很多slab緩沖區(qū)是很少用到的。

  • 而slub分配器把node結(jié)點(diǎn)的這三個(gè)鏈表精簡(jiǎn)為了一個(gè)鏈表,只保留了部分空slab鏈表,而SLUB中對(duì)于每個(gè)CPU來說已經(jīng)不使用空閑對(duì)象鏈表,而是直接使用單個(gè)slab,并且每個(gè)CPU都維護(hù)有自己的一個(gè)部分空鏈表。在slub分配器中,對(duì)于每個(gè)node結(jié)點(diǎn),也沒有了所有CPU共享的空閑對(duì)象鏈表。我們用以下圖來表示以下slab分配器和slub分配器的區(qū)別(上圖為SLAB,下圖為SLUB):

單個(gè)SLAB分配器結(jié)構(gòu)



單個(gè)SLUB分配器結(jié)構(gòu)



SLUB分配器

  • 發(fā)明SLUB分配器的主要目的就是減少slab緩沖區(qū)的個(gè)數(shù),讓更多的空閑內(nèi)存得到使用。首先,SLUB和SLAB一樣,都分為多種,同時(shí)也分為專用SLUB和普通SLUB。如TCP,UDP,dquot這些,它們都是專用SLAB,專屬于它們自己的模塊。而后面這張圖,如kmalloc-8,kmalloc-16...還有dma-kmalloc-96,dma-kmalloc-192...在這方面與SLAB是一樣的,同樣地,也是使用一個(gè)struct kmem_cache結(jié)構(gòu)來描述一個(gè)SLUB(與SLAB一樣)。并且這個(gè)struct kmem_cache與SLAB的struct kmem_cache幾乎是同一個(gè),而且對(duì)于SLAB和SLUB,向外提供的接口是統(tǒng)一的(函數(shù)名、參數(shù)以及返回值一模一樣),這樣也就讓驅(qū)動(dòng)和其他模塊在編寫代碼時(shí)無(wú)需操心系統(tǒng)使用的是SLAB還是SLUB。這是為了同一個(gè)內(nèi)核可以通過編譯選項(xiàng)使用SLAB或者SLUB。

  • SLUB分配器中的slab緩沖區(qū)結(jié)構(gòu)與SLAB分配器中的slab緩沖區(qū)的結(jié)構(gòu)也有了明顯的不同,對(duì)于SLAB分配器的slab緩沖區(qū),其結(jié)構(gòu)如下:

  • 而在SLUB分配器的slab緩沖區(qū)結(jié)構(gòu)中,已經(jīng)沒有了對(duì)象描述符數(shù)組,而freelist也拆分成了每個(gè)對(duì)象有一個(gè)指向下一個(gè)對(duì)象的指針,如下:

  • 雖然這兩個(gè)slab緩沖區(qū)的結(jié)構(gòu)上有所不同,但其實(shí)際原理還是一樣,每次分配或釋放都會(huì)設(shè)置對(duì)象的下個(gè)空閑對(duì)象指針,讓其指向正確的位置。有疑問的同學(xué)可以看看我之前寫的linux內(nèi)存源碼分析 - SLAB分配器概述。在初始化一個(gè)slab緩沖區(qū)時(shí),默認(rèn)第一個(gè)空閑對(duì)象是對(duì)象0,然后對(duì)象0后面跟著的下一個(gè)空閑對(duì)象指針指向?qū)ο?,對(duì)象1的空閑對(duì)象指針指向?qū)ο?,以此類推。

  • 我們看看SLUB分配器的描述符,struct kmem_cache結(jié)構(gòu):

  • 掃一下整個(gè)kmem_cache結(jié)構(gòu),知識(shí)點(diǎn)最重要的有4個(gè):每CPU對(duì)應(yīng)的cpu_slab結(jié)構(gòu),每個(gè)node結(jié)點(diǎn)對(duì)應(yīng)的kmem_cache_node結(jié)構(gòu),slub重用以及struct kmem_cache_order_objects結(jié)構(gòu)對(duì)應(yīng)的oo,max,min這三個(gè)值。

  • 除去以上4個(gè)知識(shí)點(diǎn),我們先簡(jiǎn)單說說kmem_cache中的一些成員變量:

  1. size:size = 對(duì)象大小 + 對(duì)象后面緊跟的下個(gè)空閑對(duì)象指針。

  2. object_size:對(duì)象大小。

  3. offset:對(duì)象首地址 + offset = 下個(gè)空閑對(duì)象指針地址

  4. min_partial:node結(jié)點(diǎn)中部分空slab緩沖區(qū)數(shù)量不能小于這個(gè)值,如果小于這個(gè)值,空閑slab緩沖區(qū)則不能夠進(jìn)行釋放,而是將空閑slab加入到node結(jié)點(diǎn)的部分空slab鏈表中。

  5. cpu_partial:同min_partial類似,只是這個(gè)值表示的是空閑對(duì)象數(shù)量,而不是部分空slab數(shù)量,即CPU的空閑對(duì)象數(shù)量不能小于這個(gè)值,小于的情況下要去對(duì)應(yīng)node結(jié)點(diǎn)的部分空鏈表中獲取若干個(gè)部分空slab。

  6. name:該kmem_cache的名字。

  • 我們?cè)賮砜纯磗truct kmem_cache_cpu __percpu *cpu_slab,對(duì)于同一種kmem_cache來說,每個(gè)CPU對(duì)應(yīng)有自己的struct kmem_cache_cpu結(jié)構(gòu),這個(gè)結(jié)構(gòu)如下:

  • 在此結(jié)構(gòu)中主要注意有個(gè)partial部分空slab鏈表以及page指針,page指針指向當(dāng)前使用的slab緩沖區(qū)描述符,內(nèi)核中slab緩沖區(qū)描述符與頁(yè)描述符共用一個(gè)struct page結(jié)構(gòu)。SLUB分配器與SLAB分配器有一部分不同就在此,SLAB分配器的每CPU結(jié)構(gòu)中保存的是空閑對(duì)象鏈表,而SLUB分配器的每CPU結(jié)構(gòu)中保存的是一個(gè)slab緩沖區(qū)。而對(duì)于tid,它主要用于檢查是否有并發(fā),對(duì)于一些操作,操作前讀取其值,操作結(jié)束后再檢查其值是否與之前讀取的一致,非一致則要進(jìn)行一些相應(yīng)的處理,這個(gè)tid一般是遞增狀態(tài),每分配一次對(duì)象加1。這個(gè)結(jié)構(gòu)說明了一個(gè)問題,就是每個(gè)CPU有自己當(dāng)前使用的slab緩沖區(qū),CPU0不能夠使用CPU1所在使用的slab緩存,CPU1也不能夠使用CPU0正在使用的slab緩存。而CPU從node獲取slab緩沖區(qū)時(shí),一般傾向于從該CPU所在的node結(jié)點(diǎn)上分配,如果該node結(jié)點(diǎn)沒有空閑的內(nèi)存,則根據(jù)memcg以及node結(jié)點(diǎn)的zonelist從其他node獲取slab緩沖區(qū)。這些具體可以在代碼中見到。


  • 我們?cè)倏纯磌mem_cache_node結(jié)構(gòu):

  • 這個(gè)結(jié)構(gòu)中我們只需要看#ifdef CONFIG_SLUB部分,這個(gè)結(jié)構(gòu)里正常情況下只有一個(gè)node結(jié)點(diǎn)部分空slab鏈表partial,如果在編譯內(nèi)核時(shí)選擇了CONFIG_SLUB_DEBUG選項(xiàng),則會(huì)有個(gè)node結(jié)點(diǎn)滿slab鏈表。對(duì)于SLAB分配器,SLUB分配器在這個(gè)結(jié)構(gòu)也做出了相應(yīng)的變化,去除了滿slab緩沖區(qū)鏈表和空閑slab緩沖區(qū)鏈表,只使用了一個(gè)部分空slab緩沖區(qū)鏈表。對(duì)于所有的CPU來說,它們可以使用這個(gè)node結(jié)點(diǎn)里面部分空鏈表中保存的那些slab緩沖區(qū),當(dāng)它們需要使用時(shí),要先將緩沖區(qū)拿到CPU對(duì)應(yīng)自己的鏈表或者當(dāng)前使用中,也就是說node結(jié)點(diǎn)上部分空slab緩沖區(qū)同一個(gè)時(shí)間只能讓一個(gè)CPU使用。

  • 而關(guān)于slub重用,這里只做一個(gè)簡(jiǎn)單的解釋,其作用是為了減少slub的種類,比如我有個(gè)kmalloc-8類型的slub,里面每個(gè)對(duì)象大小是8,而我某個(gè)驅(qū)動(dòng)想申請(qǐng)自己所屬的slub,其對(duì)象大小是6,這時(shí)候系統(tǒng)會(huì)給驅(qū)動(dòng)一個(gè)假象,讓驅(qū)動(dòng)申請(qǐng)了自己專屬的slub,但系統(tǒng)實(shí)際把kmalloc-8這個(gè)類型的slub返回給了驅(qū)動(dòng),之后驅(qū)動(dòng)中分配對(duì)象時(shí)實(shí)際上就是從kmalloc-8中分配對(duì)象,這就是slub重用,將相近大小的slub共用一個(gè)slub類型,雖然會(huì)造成一些內(nèi)碎片,但是大大減少了slub種類過多以及減少使用了跟多的內(nèi)存。

  • 最后說說struct kmem_cache_order_objects結(jié)構(gòu)對(duì)應(yīng)的oo,max,min這三個(gè)值,struct kmem_cache_order_objects結(jié)構(gòu)實(shí)際上就是一個(gè)unsigned long,這個(gè)結(jié)構(gòu)有兩個(gè)作用,保存一個(gè)slab緩沖區(qū)占用頁(yè)框的order值和一個(gè)slab緩沖區(qū)對(duì)象數(shù)量的值。當(dāng)kmem_cache需要?jiǎng)?chuàng)建一個(gè)新的slab緩沖區(qū)時(shí),會(huì)使用它們當(dāng)中保存的oder值去申請(qǐng)2的order次方個(gè)數(shù)的頁(yè)框。oo是一個(gè)默認(rèn)值,在大多數(shù)情況下創(chuàng)建一個(gè)新的slab緩沖區(qū)時(shí)會(huì)用oo中的值來申請(qǐng)頁(yè)框,而min是在oo申請(qǐng)失敗的情況下使用,它是一個(gè)比oo更小的值,當(dāng)伙伴系統(tǒng)拿不出oo中指定的數(shù)量的頁(yè)框,會(huì)嘗試向伙伴系統(tǒng)申請(qǐng)min中指定的頁(yè)框數(shù)量(這個(gè)slab緩沖區(qū)連續(xù)頁(yè)框數(shù)量少,對(duì)象數(shù)量也會(huì)少)。而max的值是在做slab緩沖區(qū)壓縮時(shí)使用,其作用更多的是作為一個(gè)安全值,在這個(gè)kmem_cache中所有slab緩沖區(qū)的objects數(shù)量都不會(huì)大于max中的值。所有情況都是max >= oo > min。


  • 現(xiàn)在,我們描述一下SLUB分配器是如何運(yùn)作的,kmem_cache初始化后其是沒有slab緩沖區(qū)的,當(dāng)其他模塊需要從此kmem_cache中申請(qǐng)一個(gè)對(duì)象時(shí),kmem_cache會(huì)從伙伴系統(tǒng)獲取連續(xù)的頁(yè)框作為一個(gè)slab緩沖區(qū),然后通過kmem_cache中的cotr函數(shù)指針指向的構(gòu)造函數(shù)構(gòu)造初始化這個(gè)slab緩沖區(qū)后,將其設(shè)置為該cpu的當(dāng)前使用slab緩沖區(qū),當(dāng)此slab緩沖區(qū)使用完后,外部模塊在申請(qǐng)對(duì)象時(shí),會(huì)把這個(gè)滿的slab緩沖區(qū)移除,再?gòu)幕锇橄到y(tǒng)獲取一段連續(xù)頁(yè)框作為一個(gè)新的空閑slab緩沖區(qū),也是設(shè)置為該CPU當(dāng)前使用的slab緩沖區(qū)。而那些滿slab緩沖區(qū)中有對(duì)象釋放時(shí),SLUB分配器優(yōu)先把這些緩沖區(qū)放入該CPU對(duì)應(yīng)的部分空slab鏈表。而當(dāng)一個(gè)部分空slab通過釋放對(duì)象成為了一個(gè)空閑slab緩沖區(qū)時(shí),SLUB分配器會(huì)視情況而定將此空閑slab釋放還是加入到node結(jié)點(diǎn)的部分空slab鏈表中。

  • 我們先看看一個(gè)slub初始化結(jié)束的情況:


  • 初始化完成后,slub中并沒有一個(gè)slab緩沖區(qū),只有在第一次申請(qǐng)時(shí),才會(huì)從伙伴系統(tǒng)中獲取一段連續(xù)頁(yè)框作為一個(gè)slab緩沖區(qū),如下:


  • 這時(shí)候當(dāng)前CPU獲得了一個(gè)空閑slab緩沖區(qū),并將其中的一個(gè)空閑對(duì)象分配出去,而下次申請(qǐng)對(duì)象時(shí)也會(huì)從該slab緩沖區(qū)中獲取對(duì)象,直到此緩沖區(qū)中對(duì)象用完為止。


  • 上面描述的是初始化完成后第一次申請(qǐng)對(duì)象的情況,現(xiàn)在我們描述一下運(yùn)行時(shí)申請(qǐng)對(duì)象的情況,一種情況是當(dāng)前CPU使用的slab緩沖區(qū)有多余的空閑對(duì)象,這樣直接從這些多余的空閑對(duì)象中分配一個(gè)出去即可,這種情況很簡(jiǎn)單。我們著重說明CPU使用的slab緩沖區(qū)沒有多余的空閑對(duì)象的情況,這種情況又分為CPU的部分空slab鏈表是否為空的情況,如果CPU部分空slab鏈表不為空,則CPU會(huì)將當(dāng)前使用的滿slab移除,并從CPU的部分空slab鏈表中獲取一個(gè)部分空的slab緩沖區(qū),并設(shè)置為CPU當(dāng)前使用的slab緩沖區(qū),如下圖:


  • 如果node的部分空鏈表和CPU的部分空鏈表都為空的情況,那就與我們第一次申請(qǐng)對(duì)象的情況一樣,直接從伙伴系統(tǒng)中獲取連續(xù)頁(yè)框用于一個(gè)slab緩沖區(qū)。


  • 現(xiàn)在我們?cè)僬f說CPU當(dāng)前使用的slab已滿,CPU的部分空slab鏈表為空的情況,這種情況下,會(huì)從node結(jié)點(diǎn)的部分空slab鏈表獲取若干個(gè)部分空slab緩沖區(qū),將它們放入CPU的部分空slab鏈表中,獲取的slab緩沖區(qū)個(gè)數(shù)根據(jù)一個(gè)規(guī)則就是:cpu空閑的對(duì)象數(shù)量必須要大于kmem_cache中的cpu_partial的值的一半。具體如下:


  • 各種情況的申請(qǐng)對(duì)象都已經(jīng)說明了,接下來我們說說釋放對(duì)象的情況,釋放對(duì)象也分很多種,我們先說說最簡(jiǎn)單的一種釋放情況,就是部分空的slab釋放其中一個(gè)使用著的對(duì)象,釋放后這個(gè)部分空slab還是部分空slab(有些部分空slab只使用了一個(gè)對(duì)象,釋放這個(gè)對(duì)象后就變?yōu)榭臻eslab),這些部分空slab可能處于CPU當(dāng)前使用slab,CPU部分空鏈表,node部分空鏈表中,但是它們的處理都是一樣的,直接釋放掉該對(duì)象即可,如下:


  • 另一種情況是滿slab緩沖區(qū)釋放對(duì)象后變?yōu)榱瞬糠挚誷lab緩沖區(qū),這種情況下系統(tǒng)會(huì)將此部分空slab緩沖區(qū)放入CPU的部分空鏈表中,如下:


  • 最后一種釋放情況就是部分空slab釋放一個(gè)對(duì)象后轉(zhuǎn)變成了空閑slab緩沖區(qū),而對(duì)于這個(gè)空閑slab緩沖區(qū)的處理,系統(tǒng)首先會(huì)檢查node部分空鏈表中slab緩沖區(qū)的個(gè)數(shù),如果node部分空鏈表中slab緩沖區(qū)數(shù)量小于kmem_cache中的min_partial,則將這個(gè)空閑slab緩沖區(qū)放入node部分空鏈表中。否則釋放此空閑slab,將其占用頁(yè)框返回伙伴系統(tǒng)中。我們知道部分空slab有可能存在于3個(gè)地方,CPU當(dāng)前使用的slab緩沖區(qū),CPU部分空鏈表,node部分空鏈表,這三個(gè)地方對(duì)于這種情況下的處理都是一樣的,如下:


  • 這樣看來只有空閑的slab緩沖區(qū)會(huì)被放入node結(jié)點(diǎn)的部分空鏈表中,這只是從釋放對(duì)象的角度看是這樣的,當(dāng)刷新kmem_cache時(shí),會(huì)將kmem_cache中所有的slab緩沖區(qū)放回到node結(jié)點(diǎn)的部分空鏈表(也包括當(dāng)前CPU使用的slab緩沖區(qū)),這種情況node結(jié)點(diǎn)的部分空鏈表就會(huì)有部分空slab緩沖區(qū)了。而還有一種情況就是編譯時(shí)禁用了CPU的部分空鏈表,即CPU只有一個(gè)當(dāng)前使用的slab緩沖區(qū),這樣其他的部分空緩沖區(qū)都會(huì)保存在node結(jié)點(diǎn)的部分空鏈表上,更多詳細(xì)細(xì)節(jié)請(qǐng)看內(nèi)核源碼中的mm/slub.c文件。

slab緩沖區(qū)壓縮技術(shù)

  • 說是壓縮技術(shù),其實(shí)就是把kmem_cache中所有的slab緩沖區(qū)放回到node結(jié)點(diǎn)的部分空鏈表中(包括所有CPU當(dāng)前正在使用的slab),然后node結(jié)點(diǎn)的部分空鏈表中的空閑的slab緩沖區(qū)釋放掉,然后將node結(jié)點(diǎn)中的其他部分空slab緩沖區(qū)按照空閑對(duì)象數(shù)量進(jìn)行重新排列,把空閑數(shù)量少的放在前面,空閑數(shù)量多的放在后面,這樣空閑數(shù)量少的更容易被移去cpu的部分空鏈表。其實(shí)思想就是讓那些更容易成為滿slab的部分空slab優(yōu)先被使用??偨Y(jié)出來就是釋放空閑slab和對(duì)部分空slab排序。

  • 我們知道,在node結(jié)點(diǎn)的部分空鏈表中,slab緩沖區(qū)數(shù)量少于kmem_cache中的min_partial的值時(shí),即使空閑slab緩沖區(qū)也不會(huì)被釋放,而是放入node結(jié)點(diǎn)部分空鏈表中,這樣一來之后會(huì)有一些空閑slab緩沖區(qū)無(wú)法自動(dòng)釋放回伙伴系統(tǒng),壓縮技術(shù)就是在系統(tǒng)內(nèi)存緊急時(shí)會(huì)去釋放這些空閑的伙伴系統(tǒng),然后對(duì)其他部分空的slab緩沖區(qū)重新排列。代碼如下:


一文給你解決linux內(nèi)存源碼分析- SLUB分配器概述(超詳細(xì))的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
隆尧县| 凤城市| 铜陵市| 无为县| 长治县| 临澧县| 秀山| 阳江市| 虞城县| 江陵县| 元阳县| 峨山| 阿拉善左旗| 牡丹江市| 鄄城县| 广饶县| 湟源县| 洛宁县| 新密市| 靖州| 布拖县| 井陉县| 将乐县| 翼城县| 潼南县| 沅江市| 九台市| 石首市| 邢台市| 平泉县| 海南省| 祥云县| 台东市| 敖汉旗| 乐平市| 江津市| 泾源县| 石门县| 上虞市| 泰和县| 崇礼县|