国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

深入理解Linux內(nèi)核——內(nèi)存管理(1)

這篇具有很好參考價(jià)值的文章主要介紹了深入理解Linux內(nèi)核——內(nèi)存管理(1)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

提要:本系列文章主要參考MIT 6.828課程以及兩本書(shū)籍《深入理解Linux內(nèi)核》 《深入Linux內(nèi)核架構(gòu)》對(duì)Linux內(nèi)核內(nèi)容進(jìn)行總結(jié)。

內(nèi)存管理的實(shí)現(xiàn)覆蓋了多個(gè)領(lǐng)域:

  1. 內(nèi)存中的物理內(nèi)存頁(yè)的管理
  2. 分配大塊內(nèi)存的伙伴系統(tǒng)
  3. 分配較小內(nèi)存的slab、slub、slob分配器
  4. 分配非連續(xù)內(nèi)存塊的vmalloc分配器
  5. 進(jìn)程的地址空間

傳統(tǒng)的內(nèi)存管理主要包括段式存儲(chǔ)、頁(yè)式存儲(chǔ)段頁(yè)式存儲(chǔ),這里我們會(huì)以這部分開(kāi)始,逐步介紹Linux內(nèi)核中的內(nèi)存管理,而要學(xué)習(xí)內(nèi)存管理,首先需要了解內(nèi)存尋址。所以本節(jié)內(nèi)容主要講解內(nèi)存尋址的相關(guān)知識(shí),并介紹Linux內(nèi)核中的段、頁(yè)式存儲(chǔ)。

內(nèi)存地址

在編程過(guò)程中,難免需要通過(guò)內(nèi)存地址來(lái)訪問(wèn)內(nèi)存中的某些內(nèi)容,那么這個(gè)過(guò)程中地址是如何映射到對(duì)應(yīng)的物理單元的呢?解決這一問(wèn)題首先要區(qū)分三種不同的地址:

  1. 邏輯地址:邏輯地址是包含在機(jī)器語(yǔ)言指令中用來(lái)指定一個(gè)操作數(shù)或者一條指令的地址。每一個(gè)邏輯地址都由一個(gè)段和一個(gè)偏移量組成,偏移量指明了從段開(kāi)始的地方到實(shí)際地址之間的距離。(這在分段結(jié)構(gòu)中表現(xiàn)的極為明顯)。
  2. 線性地址:線性地址是一個(gè)32位的無(wú)符號(hào)數(shù),可以用來(lái)表示高達(dá)4GB的地址。線性地址通常使用十六進(jìn)制數(shù)字表示,值的范圍從0x00000000到0xffffffff(常用于頁(yè)式存儲(chǔ))。
  3. 物理地址:用于內(nèi)存芯片級(jí)內(nèi)存單元尋址。它們與微處理器的地址引腳發(fā)送到內(nèi)存總線上的電信號(hào)相對(duì)應(yīng)。物理地址由32位或36位無(wú)符號(hào)整數(shù)表示。

內(nèi)存控制單元(MMU)通過(guò)分段單元(硬件設(shè)備)將邏輯地址轉(zhuǎn)化為線性地址。使用分頁(yè)單元(硬件設(shè)備)把線性地址轉(zhuǎn)化為物理地址:

               --------                     -------
|邏輯地址| --> |分段單元| --> |線性地址| --> |分頁(yè)單元| --> |物理地址|
               --------                     -------

從格式上簡(jiǎn)單區(qū)別邏輯地址與線性地址,邏輯地址包含了兩部分:段和偏移量(注意這兩者是分開(kāi)的),而線性地址知識(shí)一個(gè)32位無(wú)符號(hào)數(shù),雖然后期也會(huì)根據(jù)地址的位數(shù)再次進(jìn)行劃分(詳見(jiàn)分頁(yè)部分),但終歸只是一個(gè)線性的無(wú)符號(hào)數(shù)。

段式存儲(chǔ)

硬件將邏輯地址轉(zhuǎn)化為線性地址主要由分段單元完成該任務(wù)。邏輯地址由兩部分組成:

  1. 段標(biāo)識(shí)符:該字段是一個(gè)16位長(zhǎng)的字段,稱為段選擇符,負(fù)責(zé)從眾多段中選擇出正確的段,因?yàn)?code>段信息會(huì)存儲(chǔ)在一張段描述符表中,因此需要通過(guò)段選擇符從段描述符表中索引找到正確的段描述符。
  2. 指定段內(nèi)相對(duì)地址的偏移量:32位長(zhǎng)的字段(因?yàn)橐粋€(gè)段可能很長(zhǎng),因此偏移量要足夠大)

段選擇符格式如下:

        15        3     2 1 0
          --------  ----  ---
段選擇符 |  index  |  TL | RPL|
          --------  ----  ---

對(duì)于每個(gè)字段的含義,后續(xù)在詳細(xì)講解通過(guò)段選擇符尋找對(duì)應(yīng)段時(shí)會(huì)對(duì)每個(gè)字段給出解釋。

段選擇符被存放在段寄存器中,這使得可以方便快速地找到段選擇符,段寄存器主要包括6個(gè),分別為cs,ss,ds,es,fs,gs。其中有3個(gè)具有專門(mén)的用途:

段寄存器 描述
cs 代碼段寄存器,指向包含指令序列的段
ss 棧段寄存器,指向包含當(dāng)前程序棧的段
ds 數(shù)據(jù)段寄存器,指向包含靜態(tài)數(shù)據(jù)或者全局?jǐn)?shù)據(jù)段

其他3個(gè)段寄存器作一般用途,可以指向任意的數(shù)據(jù)段。

注意:cs寄存器還有一個(gè)很重要的功能:它含有一個(gè)兩位的字段,用以指明CPU的當(dāng)前特權(quán)級(jí)(GPL)。值為0代表最高優(yōu)先級(jí),而值為3代表最低優(yōu)先級(jí)。Linux只用0級(jí)和3級(jí),分別稱為內(nèi)核態(tài)和用戶態(tài)。

段描述符

剛才提到過(guò),每個(gè)段由一個(gè)8字節(jié)的段描述符表示,它描述了段的特征。段描述符放在全局描述符表(GDT)或者局部描述符表里(LDT)中(前面提到過(guò))。通常只定義一個(gè)GDT,而每個(gè)進(jìn)程除了存放在GDT中的段之外如果還需要?jiǎng)?chuàng)建附加段,就會(huì)創(chuàng)建自己的LDT。GDT在主存中的地址和大小存放在gdtr控制寄存器中,當(dāng)前正被使用的LDT地址和大小存放在ldtr寄存器中。如下給出一個(gè)全局段描述符例子:

深入理解Linux內(nèi)核——內(nèi)存管理(1)

在段描述符表中通常會(huì)使用如下幾種段描述符(簡(jiǎn)單了解各個(gè)段的作用即下面第一個(gè)表就好,具體字段的名稱可以用到再回來(lái)查):

描述符名稱 描述
代碼段描述符 這個(gè)段描述符代表一個(gè)代碼段,它可以放在GDT或LDT中。該描述符置S標(biāo)志位1(非系統(tǒng)段)
數(shù)據(jù)段描述符 這個(gè)段描述符代表一個(gè)數(shù)據(jù)段,它可以放在GDT或LDT中。該描述符置S標(biāo)志為1。棧段是通過(guò)一般的數(shù)據(jù)段實(shí)現(xiàn)的。
任務(wù)狀態(tài)段描述符(TSSD) 這個(gè)段描述符代表一個(gè)任務(wù)狀態(tài)段(Task State Segment, TSS),也就是說(shuō)這個(gè)段用于保存處理器寄存器的內(nèi)容。它只能出現(xiàn)在GDT中。根據(jù)相應(yīng)的進(jìn)程是否正在CPU上運(yùn)行,其Type字段的值分別為11或9。這個(gè)描述符的S標(biāo)志置為0。
局部描述符表描述符(LDTD) 這個(gè)段描述符代表一個(gè)包含LDT的段,它只出現(xiàn)在GDT中。相應(yīng)的Type字段的值為2,s標(biāo)志置為0

段描述符格式如下:

深入理解Linux內(nèi)核——內(nèi)存管理(1)

段描述符中各個(gè)字段含義如下:

字段表 描述
Base 包含段的首字節(jié)的線性地址
G 粒度標(biāo)志:如果該位清0,則段大小以字節(jié)為單位,否則以4096的倍數(shù)計(jì)
Limit 存放段中最后一個(gè)內(nèi)存單元的偏移量,從而決定段的長(zhǎng)度。如果G被置為0,則一個(gè)段的大小在1個(gè)字節(jié)到1MB之間變化;否則,則在4KB到4GB之間變化。
S 系統(tǒng)標(biāo)志:如果它被清0,則這是一個(gè)系統(tǒng)段,存儲(chǔ)諸如LDT這種關(guān)鍵的數(shù)據(jù)結(jié)構(gòu),否則它是一個(gè)普通的代碼段或者數(shù)據(jù)段。
Type 描述了段的類型特征和他的存取權(quán)限
DPL 描述符特權(quán)級(jí)(Descriptor Privilege Level)字段:用于限制對(duì)這個(gè)段的存取。它表示為訪問(wèn)這個(gè)段而要求的CPU最小的優(yōu)先級(jí)。因此,DPL設(shè)為0的段只能當(dāng)CPL為0時(shí)(即在內(nèi)核態(tài))才是可以訪問(wèn)的,而DPL設(shè)為3的段對(duì)任何CPL值都是可以訪問(wèn)的。
P Segment-Present標(biāo)志:等于0表示段當(dāng)前不在主存中。Linux總是把這個(gè)標(biāo)志(第47位)設(shè)為1,因?yàn)樗鼜膩?lái)不把整個(gè)段交換到磁盤(pán)上去。
D或B 成為D或B的標(biāo)志,取決于是代碼段還是數(shù)據(jù)段。D或B的含義在兩種情況下稍微有區(qū)別,但是如果段偏移量的地址是32位長(zhǎng),就基本上把它置為1,如果這個(gè)偏移量是16位長(zhǎng),它被清0。
AVL標(biāo)志 可以由操作系統(tǒng)使用,但是被Linux忽略

快速訪問(wèn)段描述符

在本節(jié)主要介紹分段單元將邏輯地址轉(zhuǎn)化為線性地址的過(guò)程。我們知道邏輯地址主要包括:16位的段選擇符和32位的段偏移量,段選擇符存放在段寄存器中。段選擇符格式如下:

        15        3     2 1 0
          --------  ----  ---
段選擇符 |  index  |  TL | RPL|
          --------  ----  ---

這里我們需要了解3個(gè)字段的含義:

字段名 描述
index 指定了放在GDT或者LDT中相應(yīng)的段描述符的入口
TI TI(Table Indicator)標(biāo)志:指明段描述符是在GDT中(TI = 0)或在LDT中(TI=1)
RPL 請(qǐng)求者特權(quán)級(jí):當(dāng)相應(yīng)的段選擇符裝入到cs寄存器中時(shí)指示出CPU當(dāng)前的特權(quán)級(jí);它還可以用于在訪問(wèn)數(shù)據(jù)段時(shí)有選擇地削弱處理器的特權(quán)級(jí)。

由于一個(gè)段描述符是8個(gè)字節(jié)長(zhǎng),因此它在GDT或LDT內(nèi)的相對(duì)地址是由段選擇符的最高13位的值乘以8得到的(8=2^3,13+3=16)。例如如果GDT在0x00020000(這個(gè)值保存在gdtr寄存器中),切由段選擇符所指定的索引號(hào)為2,那么相應(yīng)的段描述符地址為0x00020000+(2*8)0x00020010。

邏輯地址轉(zhuǎn)換為線性地址流程如下圖:

深入理解Linux內(nèi)核——內(nèi)存管理(1)

  1. 先檢查段選擇符的TI字段,以確定段描述符保存在哪一個(gè)描述符表(GDT、LDT)中。
  2. 從段選擇符的index字段計(jì)算段描述符的地址,index字段值乘以8(一個(gè)段描述符大?。?,這個(gè)結(jié)果與gdtr或ldtr寄存器中的內(nèi)容相加
  3. 把邏輯地址的偏移量與段描述符的Base字段的值相加就得到了線性地址。

操作系統(tǒng)課本中的介紹通常如下,可以與之進(jìn)行對(duì)比:

深入理解Linux內(nèi)核——內(nèi)存管理(1)

注意:GDT的第一項(xiàng)總是設(shè)置為0。這就確保空段選擇符的邏輯地址會(huì)被認(rèn)為是無(wú)效的,因此引起一個(gè)處理器異常。能夠保存在GDT中的段描述符的最大數(shù)目是8191,即2^13-1。

最后,由于整個(gè)地址轉(zhuǎn)換過(guò)程中,前兩個(gè)過(guò)程,即訪問(wèn)GDT、LDT中段描述符的過(guò)程是比較耗時(shí)的,為了加速該過(guò)程,80x86處理器提供了一種附加的非編程的寄存器供6個(gè)可編程的段寄存器使用。每個(gè)非編程寄存器含有8個(gè)字節(jié)的段描述符。通過(guò)這個(gè)非編程寄存器,達(dá)到了如下功能:每當(dāng)一個(gè)段描述符被裝入段寄存器時(shí),相應(yīng)的段描述符就由內(nèi)存裝入到對(duì)應(yīng)的非編程CPU寄存器中,從那時(shí)起,針對(duì)那個(gè)段的邏輯地址轉(zhuǎn)換就可以不訪問(wèn)主存中的GDT或LDT,只有在段寄存器內(nèi)容改變時(shí),才有必要訪問(wèn)GDT或LDT。

Linux中的分段

Linux以非常有限的方式使用分段,分段可以給一個(gè)進(jìn)程分配不同的線性地址空間,而分頁(yè)可以把同一線性地址空間映射到不同的物理空間,與分段相比,Linux更喜歡使用分頁(yè)方式,因?yàn)椋?/p>

  1. 當(dāng)所有進(jìn)程使用相同的段寄存器時(shí),內(nèi)存管理變得更簡(jiǎn)單,也就是說(shuō)他們能共享同樣的一組線性地址。
  2. Linux設(shè)計(jì)目標(biāo)之一時(shí)可以把它移植到絕大多數(shù)的處理器平臺(tái)上。然而,RISC體系結(jié)構(gòu)對(duì)分段的支持很有限。

2.6版的Linux只有在80x86結(jié)構(gòu)下才需要使用分段。

運(yùn)行在用戶態(tài)的所有Linux進(jìn)程都使用一對(duì)相同的段來(lái)對(duì)指令和數(shù)據(jù)尋址。這兩個(gè)段就是所謂的用戶代碼段用戶數(shù)據(jù)段。類似的運(yùn)行在內(nèi)核態(tài)的所有Linux進(jìn)程都使用一對(duì)相同的段對(duì)指令和數(shù)據(jù)尋址,他們分別是內(nèi)核代碼段內(nèi)核數(shù)據(jù)段。下表顯示了這四個(gè)重要段的段描述符字段的值。

Base G Limit S Type DPL D/B P
用戶代碼段 0x00000000 1 0xfffff 1 10 3 1 1
用戶數(shù)據(jù)段 0x00000000 1 0xfffff 1 2 3 1 1
內(nèi)核代碼段 0x00000000 1 0xfffff 1 10 0 1 1
內(nèi)核數(shù)據(jù)段 0x00000000 1 0xfffff 1 2 0 1 1

上面4個(gè)段,G標(biāo)志都設(shè)置為1,即limit以4096為單位。與段相關(guān)的線性地址從0開(kāi)始,達(dá)到2^32-1的尋址限長(zhǎng),這意味著在用戶態(tài)或內(nèi)核態(tài)下的所有進(jìn)程可以使用相同的邏輯地址。

所有段都從0x00000000開(kāi)始,表示Linux下邏輯地址和線性地址是一致的,即邏輯地址的偏移量字段的值與相應(yīng)的線性地址的值總是一致的。

那如何區(qū)分這4個(gè)段呢?

如前所述,CPU的當(dāng)前特權(quán)級(jí)(CPL)反映了進(jìn)程是在用戶態(tài)還是內(nèi)核態(tài),并由存放在cs寄存器中的段選擇符的RPL字段指定。只要當(dāng)前特權(quán)級(jí)被改變,一些段寄存器必須相應(yīng)地更新。例如,當(dāng)CPL=3時(shí)(用戶態(tài)),ds寄存器必須含有用戶數(shù)據(jù)段的段選擇符,而當(dāng)CPL=0時(shí),ds寄存器必須含有內(nèi)核數(shù)據(jù)段的段選擇符。

頁(yè)式存儲(chǔ)

分頁(yè)單元(paging unit)把線性地址轉(zhuǎn)換成物理地址。其中的一個(gè)關(guān)鍵任務(wù)是把所請(qǐng)求的訪問(wèn)類型與線性地址的訪問(wèn)權(quán)限項(xiàng)比較,如果這次內(nèi)存訪問(wèn)是無(wú)效的,就產(chǎn)生一個(gè)缺頁(yè)異常。

為了效率起見(jiàn),線性地址被分成以固定長(zhǎng)度為單位的組,稱為頁(yè)(page)。頁(yè)內(nèi)部連續(xù)的線性地址被映射到連續(xù)的物理地址中。這樣,內(nèi)核可以指定一個(gè)頁(yè)的物理地址和其存儲(chǔ)權(quán)限,而不用指定頁(yè)所包含的全部線性地址的存取權(quán)限。

分頁(yè)單元把所有的RAM分成固定長(zhǎng)度的頁(yè)框(有時(shí)叫做物理頁(yè))。每一個(gè)頁(yè)框包含一個(gè)頁(yè)(page),也就是說(shuō)一個(gè)頁(yè)框的長(zhǎng)度與一個(gè)頁(yè)的長(zhǎng)度一致。頁(yè)框是主存的一部分,因此也是一個(gè)存儲(chǔ)區(qū)域。

把線性地址映射到物理地址的數(shù)據(jù)結(jié)構(gòu)稱為頁(yè)表。頁(yè)表存放在主存中,并在啟動(dòng)分頁(yè)單元之前必須由內(nèi)核對(duì)頁(yè)表進(jìn)行適當(dāng)?shù)某跏蓟?/p>

從80386開(kāi)始,所有的80x86處理器都支持分頁(yè),它通過(guò)設(shè)置cr0寄存器的PG標(biāo)志啟用。當(dāng)PG=0時(shí),線性地址就被解釋成物理地址。

常規(guī)分頁(yè)

從80386起,Intel處理器的分頁(yè)單元處理4KB的頁(yè)。

32位的線性地址被分為3個(gè)域(線性地址是根據(jù)位數(shù)劃分的,是隱式劃分的功能):

  • Directory(目錄):最高10位。
  • Table(頁(yè)表):中間10位。
  • Offset(偏移量):最低12位。

線性地址轉(zhuǎn)換為物理地址時(shí),需要依賴兩張表:頁(yè)目錄表和頁(yè)表。轉(zhuǎn)換過(guò)程如下圖:

深入理解Linux內(nèi)核——內(nèi)存管理(1)

每個(gè)活動(dòng)進(jìn)程必須有一個(gè)分配給它的頁(yè)目錄,正在使用的頁(yè)目錄的物理地址存放在控制寄存器cr3中。線性地址內(nèi)的Directory字段決定頁(yè)目錄表中目錄項(xiàng),而目錄項(xiàng)指向適當(dāng)?shù)捻?yè)表。地址的Table字段依次又決定頁(yè)表中的表項(xiàng),而表項(xiàng)含有頁(yè)所在頁(yè)框的物理地址。Offset字段決定頁(yè)框內(nèi)的相對(duì)位置,由于它是12位長(zhǎng),故每一頁(yè)含有4096字節(jié)的數(shù)據(jù)。

頁(yè)目錄項(xiàng)表和頁(yè)表中的每項(xiàng)具有相同的結(jié)構(gòu)(可以先暫時(shí)跳過(guò),碰到使用的字段可以再回來(lái)看),每項(xiàng)字段如下:

標(biāo)志 描述
Present標(biāo)志 如果被置為1,所指的頁(yè)(或頁(yè)表)就在主存中。如果該標(biāo)志為0,則這一頁(yè)不在主存中,此時(shí)這個(gè)表剩余的位可由操作系統(tǒng)用于自己的目的。如果執(zhí)行一個(gè)地址轉(zhuǎn)換所需的頁(yè)表項(xiàng)或頁(yè)目錄項(xiàng)中的Present標(biāo)志被清0,那么分頁(yè)單元就把該線性地址存放在控制寄存器cr2中,并產(chǎn)生14號(hào)異常:缺頁(yè)異常。
包含頁(yè)框物理地址最高20位的字段 由于每一個(gè)頁(yè)框有4KB的容量,它的物理地址必須是4096的倍數(shù),因此物理地址的最低12位總是為0。如果這個(gè)字段指向一個(gè)頁(yè)目錄,相應(yīng)的頁(yè)框就含有一個(gè)頁(yè)表,如果它指向一個(gè)頁(yè)表,相應(yīng)的頁(yè)框就含有一頁(yè)數(shù)據(jù)。
Accessed標(biāo)志 每當(dāng)分頁(yè)單元對(duì)應(yīng)頁(yè)框進(jìn)行尋址時(shí)就設(shè)置這個(gè)標(biāo)志。當(dāng)選出的頁(yè)被交換出去時(shí),這一標(biāo)志就可以由操作系統(tǒng)使用。分頁(yè)單元從來(lái)不重置這個(gè)標(biāo)志,而是必須由操作系統(tǒng)去做。
Dirty標(biāo)志 只應(yīng)用于頁(yè)表項(xiàng)中。每當(dāng)對(duì)一個(gè)頁(yè)框進(jìn)行寫(xiě)操作時(shí)就設(shè)置這個(gè)標(biāo)志。與Accessed標(biāo)志一樣,當(dāng)選中的頁(yè)被交換出去時(shí),這一標(biāo)志就可以由操作系統(tǒng)使用。分頁(yè)單元從來(lái)不重置這個(gè)標(biāo)志,而是必須由操作系統(tǒng)去做。
Read/Write標(biāo)志 含有頁(yè)或頁(yè)表的存取權(quán)限(Read/Write或Read)。
User/Superisor標(biāo)志 含有訪問(wèn)頁(yè)或頁(yè)表所需的特權(quán)級(jí)。
PCD和PWT標(biāo)志 控制硬件告訴緩存處理頁(yè)或頁(yè)表的方式。
Page Size標(biāo)志 只應(yīng)用于頁(yè)目錄項(xiàng)。如果設(shè)置為1,則頁(yè)目錄項(xiàng)指的是2MB或4MB的頁(yè)框。
Global標(biāo)志 只應(yīng)用于頁(yè)表項(xiàng)。這個(gè)標(biāo)志時(shí)在Pentium Pro中引入的,用來(lái)防止常用頁(yè)從TLB(快表)高速緩存中刷新出去。只有在cr4寄存器的頁(yè)全局啟動(dòng)(PGE)標(biāo)志置位時(shí)這個(gè)標(biāo)志才起作用。

這里再簡(jiǎn)單強(qiáng)調(diào)一個(gè)問(wèn)題,為何要使用多級(jí)頁(yè)表:

常規(guī)分頁(yè)的加速策略

當(dāng)今的微處理器時(shí)鐘頻率接近幾個(gè)GHz,而動(dòng)態(tài)RAM(DRAM)芯片的存取時(shí)間是時(shí)鐘周期的數(shù)百倍。這意味著,當(dāng)從RAM中取操作數(shù)或向RAM中存放結(jié)果這樣的指令執(zhí)行時(shí),CPU可能等待很長(zhǎng)時(shí)間。

硬件高速緩存

為了縮小CPU和RAM之間的速度不匹配,引入了硬件高速緩存內(nèi)存(hardware cache memory)。硬件高速緩存基于著名的局部性原理(locality principle),它表明由于程序的循環(huán)結(jié)構(gòu)及相關(guān)數(shù)組可以組織成線性數(shù)組,最近最常用的相鄰地址在最近的將來(lái)又被用到的可能性極大。80x86體系結(jié)構(gòu)中引入了一個(gè)叫行(line)的新單位。行由幾十個(gè)連續(xù)的字節(jié)組成,它們以脈沖突發(fā)模式(burst mode)在慢速DRAM和快速的用來(lái)實(shí)現(xiàn)告訴緩存的片上靜態(tài)RAM(SRAM)之間傳送,用來(lái)實(shí)現(xiàn)高速緩存。

高速緩存再被細(xì)分為行的子集。在一種極端的情況下,高速緩存可以是直接映射的(direct mapped),這時(shí)主存中的一個(gè)行總是存放在高速緩存中完全相同的位置。在另一種極端情況下,高速緩存是充分關(guān)聯(lián)的(fully associative),這意味著主存中的任意一個(gè)行可以存放在高速緩存中的任意位置。但是大多數(shù)高速緩存在某種程度上是N-路組關(guān)聯(lián)的(N-way set associative),意味著主存中的任意一個(gè)行可以存放在高速緩存N行中的任意一行中。例如,內(nèi)存中的一個(gè)行可以存放到一個(gè)2路組關(guān)聯(lián)高速緩存兩個(gè)不同的行中。

如下圖,硬件高速緩存由兩部分組成:

  1. 硬件高速緩存內(nèi)存:負(fù)責(zé)存放真正的行
  2. 高速緩存控制器:存放一個(gè)表項(xiàng)數(shù)組,每個(gè)表項(xiàng)對(duì)應(yīng)高速緩存內(nèi)存中的一個(gè)行。每個(gè)表項(xiàng)有一個(gè)標(biāo)簽(tag)和描述高速緩存行狀態(tài)的幾個(gè)標(biāo)志(flag)。這個(gè)標(biāo)簽由一些位組成,這些位讓高速緩存控制器能夠辨別由這個(gè)行當(dāng)前所映射的內(nèi)存單元。這種內(nèi)存物理地址通常分為3組:最高幾位對(duì)應(yīng)標(biāo)簽,中間幾位對(duì)應(yīng)高速緩存控制器的子集索引,最低幾位對(duì)應(yīng)行內(nèi)的偏移量。

深入理解Linux內(nèi)核——內(nèi)存管理(1)

這里引用鏈接中的一部分描述,對(duì)內(nèi)存物理地址進(jìn)行描述(否則后續(xù)的高速緩存訪問(wèn)過(guò)程可能比較難以看懂):

訪問(wèn)cache時(shí),訪問(wèn)地址可分為3個(gè)部分:偏移量Offset、索引Index和標(biāo)簽Tag。

  • Offset是塊內(nèi)地址,在地址的低幾位,因?yàn)閏ache塊一般比較大,如每個(gè)cache塊32字節(jié)或64字節(jié)。以32個(gè)字節(jié)為例,讀cache時(shí)把32個(gè)字節(jié)即256位作為一組一起都讀出來(lái),用Offset在32字節(jié)中選擇本次訪問(wèn)所需的字或雙字等;
  • Index用來(lái)索引cache,訪問(wèn)時(shí)用Index作為訪問(wèn)cache的地址。
  • 地址的高位是訪問(wèn)cache的Tag,由于cache的大小有限,每個(gè)cache行可能對(duì)應(yīng)內(nèi)存中的若干個(gè)存儲(chǔ)塊,cache中的每一行都要用tag來(lái)標(biāo)識(shí)當(dāng)前存的是哪個(gè)存儲(chǔ)塊,訪問(wèn)時(shí)用地址的Tag跟cache存的tag進(jìn)行比較,如果相等就給出命中信號(hào)hit。

了解了上面的內(nèi)容,原書(shū)中的高速緩存訪問(wèn)過(guò)程就可以很容易看懂了:

  1. 當(dāng)訪問(wèn)一個(gè)RAM存儲(chǔ)單元時(shí),CPU從物理地址中提取出子集的索引號(hào)并把子集中所有行的標(biāo)簽與物理地址的高幾位相比較。
  2. 如果發(fā)現(xiàn)某一個(gè)行的標(biāo)簽與這個(gè)物理地址的高位相同,則CPU命中一個(gè)高速緩存(cache hit);否則,高速緩存沒(méi)有命中(cache miss)。

最后給出了查找到高速緩存中具體內(nèi)容后的一些后操作:

  1. 當(dāng)命中一個(gè)高速緩存時(shí),高速緩存控制器進(jìn)行不同的操作,具體取決于存儲(chǔ)類型。
  • 對(duì)于讀操作,控制器從高速緩存行中選取數(shù)據(jù)并送到CPU寄存器;不需要訪問(wèn)RAM而節(jié)約了CPU時(shí)間。因此高速緩存系統(tǒng)起到了其應(yīng)有的作用。
  • 對(duì)于寫(xiě)操作,控制器可能采用以下兩個(gè)基本策略之一,分別稱之為通寫(xiě)和回寫(xiě)。
    • 通寫(xiě)中,控制器總是既寫(xiě)RAM也寫(xiě)高速緩存行,為了提高寫(xiě)操作的效率關(guān)閉高速緩存。
    • 回寫(xiě)方式只更新高速緩存行,不改變RAM的內(nèi)容,提供了更快的功效。當(dāng)然,回寫(xiě)結(jié)束后,RAM最終必須被更新。只有當(dāng)CPU執(zhí)行一條要求刷新高速緩存表項(xiàng)的指令時(shí),或者當(dāng)一個(gè)FLUSH硬件信號(hào)產(chǎn)生時(shí)(通常在高速緩存不命中之后),高速緩存控制器才把高速緩存行寫(xiě)回到RAM中。
  1. 當(dāng)高速緩存沒(méi)有命中時(shí),高速緩存行被寫(xiě)回到內(nèi)存中,如果有必要的話,吧正確的行從RAM中取出放到高速緩存的表項(xiàng)中。

頁(yè)目錄項(xiàng)表和頁(yè)表中的每項(xiàng)具有相同的結(jié)構(gòu) 中PCD和PWT兩個(gè)標(biāo)志用于控制上述操作:

  • PCD(Page Cache Disabit)標(biāo)志指明當(dāng)訪問(wèn)包含在這個(gè)頁(yè)框中的數(shù)據(jù)時(shí),高速緩存功能必須被啟用還是禁用。
  • PWT(page Write-Through)標(biāo)志指明當(dāng)把數(shù)據(jù)寫(xiě)到頁(yè)框時(shí),必須使用的策略是回寫(xiě)策略還是通寫(xiě)策略。

最后給出一個(gè)鏈接,較為詳細(xì)的介紹了硬件高速緩存,如果感興趣可以繼續(xù)了解。

轉(zhuǎn)換后援緩沖器(TLB)

注意:這個(gè)TLB不是Thread Local Buffer。

除了通用硬件高速緩存之外,80x86處理器還包含了另一個(gè)稱為轉(zhuǎn)換后援緩沖器或TLB(Translation Lookaside Buffer)的高速緩存用于加快線性地址的轉(zhuǎn)換。當(dāng)一個(gè)線性地址被第一個(gè)使用時(shí),通過(guò)慢速訪問(wèn)RAM中的頁(yè)表計(jì)算出相應(yīng)的物理地址。同時(shí)物理地址被存放在一個(gè)TLB表項(xiàng)(TLB entry)中,以便以后對(duì)同一個(gè)線性地址的引用可以快速地得到轉(zhuǎn)換。

在多處理器系統(tǒng)中,每個(gè)CPU都有自己的TLB,這叫做該CPU的本地TLB。與硬件高速緩存相反,TLB中的對(duì)應(yīng)項(xiàng)不必同步,這是因?yàn)檫\(yùn)行在現(xiàn)有CPU上的進(jìn)程可以使同一線性地址與不同的物理地址發(fā)生聯(lián)系。

當(dāng)CPU的cr3控制寄存器被修改時(shí),硬件自動(dòng)使本地TLB中的所有項(xiàng)都無(wú)效,這是因?yàn)樾碌囊唤M頁(yè)表被啟用而TLB指向的是舊數(shù)據(jù)。

64位系統(tǒng)中的分頁(yè)

通過(guò)上面我們可以看到32位系統(tǒng)使用兩級(jí)分頁(yè)就可以滿足需求,那64位系統(tǒng)呢?

首先假設(shè)一個(gè)大小為4KB的標(biāo)準(zhǔn)頁(yè)。因?yàn)?KB覆蓋210個(gè)地址的范圍,4KB覆蓋212個(gè)地址,所以offset字段是12位。這樣線性地址就剩下52位分配給Table和Directory字段。如果我們現(xiàn)在決定僅僅使用64位中的48位來(lái)尋址(這個(gè)限制仍然使我們自在地?fù)碛?56TB的尋址空間!),剩下的48-12=36位將被分配給Table和Directory字段。如果我們現(xiàn)在決定為兩個(gè)字段各預(yù)留18位,那么每個(gè)進(jìn)程的頁(yè)目錄和頁(yè)表都含有2^18個(gè)項(xiàng),即超過(guò)256000個(gè)項(xiàng),而一頁(yè)無(wú)法放下如此多的頁(yè)目錄項(xiàng)和頁(yè)表項(xiàng)。

因此,所有64位處理器的硬件分頁(yè)系統(tǒng)都使用了額外的分頁(yè)級(jí)別。使用的級(jí)別數(shù)量取決于處理器的類型。下表總結(jié)出了一些Linux支持64位平臺(tái)使用的硬件分頁(yè)系統(tǒng)的主要特征:

平臺(tái)名稱 頁(yè)大小 尋址使用的位數(shù) 分頁(yè)級(jí)別數(shù) 線性地址分級(jí)
alpha 8KB 43 3 10+10+10+13
ia64 4KB 39 3 9+9+9+12
x86_64 4KB 48 4 9+9+9+9+12

Linux本身提供了一種通用的分頁(yè)系統(tǒng),它適用于絕大多數(shù)所支持的硬件分頁(yè)系統(tǒng)。

Linux的分頁(yè)

Linux采用了一種同時(shí)適用于32位和64位系統(tǒng)的普通分頁(yè)模型。到2.6.10版本,Linux采用三級(jí)分頁(yè)的模型。從2.6.11版本開(kāi)始,采用了四級(jí)分頁(yè)模型,分為4種頁(yè)表如下:

  • 頁(yè)全局目錄(Page Global Directory)
  • 頁(yè)上級(jí)目錄(Page Upper Directory)
  • 頁(yè)中間目錄(Page Middle Directory)
  • 頁(yè)表(Page Table)

結(jié)構(gòu)如下圖:

深入理解Linux內(nèi)核——內(nèi)存管理(1)

頁(yè)全局目錄包含若干頁(yè)上級(jí)目錄的地址,頁(yè)上級(jí)目錄又依次包含若干頁(yè)中間目錄的地址,而頁(yè)中間目錄又包含若干頁(yè)表地址。因此線性地址被分成五個(gè)部分。上圖沒(méi)有顯示位數(shù),因?yàn)槊恳徊糠值拇笮∨c具體的計(jì)算機(jī)體系結(jié)構(gòu)有關(guān)。

對(duì)于沒(méi)有啟用物理地址擴(kuò)展的32位系統(tǒng),兩級(jí)頁(yè)表已經(jīng)足夠了。Linux通過(guò)使“頁(yè)上級(jí)目錄”位和“頁(yè)中間目錄”位全為0,從根本上取消了頁(yè)上集目錄和頁(yè)中間目錄字段。不過(guò),頁(yè)上級(jí)目錄和頁(yè)中間目錄在指針序列中的位置被保留,以便同樣的代碼在32位系統(tǒng)和64位系統(tǒng)下都能使用。內(nèi)核為頁(yè)上集目錄和頁(yè)中間目錄保留了一個(gè)位置,這是通過(guò)把它們的頁(yè)目錄項(xiàng)數(shù)設(shè)置為1,并把這兩個(gè)目錄項(xiàng)映射到全局目錄一個(gè)適當(dāng)?shù)哪夸浂鴮?shí)現(xiàn)的。

總結(jié)

本篇文章主要講解內(nèi)存地址轉(zhuǎn)換的主要內(nèi)容,并對(duì)Linux內(nèi)核對(duì)段式存儲(chǔ)和頁(yè)式存儲(chǔ)進(jìn)行了簡(jiǎn)單的了解,下一篇文章將開(kāi)始進(jìn)行真正內(nèi)存管理相關(guān)的內(nèi)容。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-635255.html

到了這里,關(guān)于深入理解Linux內(nèi)核——內(nèi)存管理(1)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 深入理解Linux虛擬內(nèi)存管理

    深入理解Linux虛擬內(nèi)存管理

    Linux 內(nèi)核設(shè)計(jì)與實(shí)現(xiàn) 深入理解 Linux 內(nèi)核 Linux 設(shè)備驅(qū)動(dòng)程序 Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解 深入理解Linux虛擬內(nèi)存管理(一) 深入理解Linux虛擬內(nèi)存管理(二) 深入理解Linux虛擬內(nèi)存管理(三) 深入理解Linux虛擬內(nèi)存管理(四) 深入理解Linux虛擬內(nèi)存管理(五) 深入理解Linux虛擬內(nèi)存

    2024年02月06日
    瀏覽(18)
  • 深入理解Linux虛擬內(nèi)存管理(六)

    深入理解Linux虛擬內(nèi)存管理(六)

    Linux 內(nèi)核設(shè)計(jì)與實(shí)現(xiàn) 深入理解 Linux 內(nèi)核 Linux 設(shè)備驅(qū)動(dòng)程序 Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解 深入理解Linux虛擬內(nèi)存管理(一) 深入理解Linux虛擬內(nèi)存管理(二) 深入理解Linux虛擬內(nèi)存管理(三) 深入理解Linux虛擬內(nèi)存管理(四) 深入理解Linux虛擬內(nèi)存管理(五) 深入理解Linux虛擬內(nèi)存

    2024年02月08日
    瀏覽(25)
  • Linux源碼解讀系列是一套深入剖析Linux內(nèi)核源碼的教程,旨在幫助讀者理解Linux操作系統(tǒng)的底層原理和工作機(jī)制

    Linux源碼解讀系列是一套深入剖析Linux內(nèi)核源碼的教程,旨在幫助讀者理解Linux操作系統(tǒng)的底層原理和工作機(jī)制

    Linux源碼解讀系列是一套深入剖析Linux內(nèi)核源碼的教程,旨在幫助讀者理解Linux操作系統(tǒng)的底層原理和工作機(jī)制。該系列教程從Linux內(nèi)核的各個(gè)模塊入手,逐一分析其源碼實(shí)現(xiàn),并結(jié)合實(shí)際應(yīng)用場(chǎng)景進(jìn)行講解。通過(guò)學(xué)習(xí)本系列,讀者可以深入了解Linux操作系統(tǒng)的底層機(jī)制,掌握

    2024年01月21日
    瀏覽(26)
  • Linux 內(nèi)核深入理解 - 緒論

    目錄 多用戶系統(tǒng) 進(jìn)程 內(nèi)核體系架構(gòu) 文件系統(tǒng)概述 Base 硬鏈接和軟鏈接 Unix文件類型 文件描述符與索引節(jié)點(diǎn) 文件操作的系統(tǒng)調(diào)用 Unix內(nèi)核簡(jiǎn)述 進(jìn)程的實(shí)現(xiàn) 可重入內(nèi)核 進(jìn)程地址空間 同步和臨界區(qū) 信號(hào)與進(jìn)程之間的通信 進(jìn)程管理 內(nèi)存管理 虛擬內(nèi)存 隨機(jī)訪問(wèn)存儲(chǔ)器的使用 內(nèi)

    2024年04月28日
    瀏覽(21)
  • 深入理解 Linux 內(nèi)核

    深入理解 Linux 內(nèi)核

    Linux 內(nèi)核設(shè)計(jì)與實(shí)現(xiàn) 深入理解 Linux 內(nèi)核 深入理解 Linux 內(nèi)核(二) Linux 設(shè)備驅(qū)動(dòng)程序 Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解 ??本文主要用來(lái)摘錄《深入理解 Linux 內(nèi)核》一書(shū)中學(xué)習(xí)知識(shí)點(diǎn),本書(shū)基于 Linux 2.6.11 版本,源代碼摘錄基于 Linux 2.6.34 ,兩者之間可能有些出入。 ??可參考 ? 1、

    2023年04月27日
    瀏覽(28)
  • 深入理解Linux 內(nèi)核追蹤機(jī)制

    深入理解Linux 內(nèi)核追蹤機(jī)制

    Linux 存在眾多 tracing tools,比如 ftrace、perf,他們可用于內(nèi)核的調(diào)試、提高內(nèi)核的可觀測(cè)性。眾多的工具也意味著繁雜的概念,諸如 tracepoint、trace events、kprobe、eBPF 等,甚至讓人搞不清楚他們到底是干什么的。本文嘗試?yán)砬暹@些概念。 ? Probe Handler 如果我們想要追蹤內(nèi)核的一

    2024年02月15日
    瀏覽(30)
  • 【深入理解Linux內(nèi)核鎖】三、原子操作

    系列文章 : 我的圈子:高級(jí)工程師聚集地 【深入理解Linux鎖機(jī)制】一、內(nèi)核鎖的由來(lái) 【深入理解Linux鎖機(jī)制】二、中斷屏蔽 【深入理解Linux鎖機(jī)制】三、原子操作 【深入理解Linux鎖機(jī)制】四、自旋鎖 【深入理解Linux鎖機(jī)制】五、衍生自旋鎖 【深入理解Linux鎖機(jī)制】六、信號(hào)

    2024年02月12日
    瀏覽(28)
  • 深入理解Linux內(nèi)核網(wǎng)絡(luò)——內(nèi)核是如何接收到網(wǎng)絡(luò)包的

    深入理解Linux內(nèi)核網(wǎng)絡(luò)——內(nèi)核是如何接收到網(wǎng)絡(luò)包的

    系列文章: 深入理解Linux網(wǎng)絡(luò)——內(nèi)核是如何接收到網(wǎng)絡(luò)包的 深入理解Linux網(wǎng)絡(luò)——內(nèi)核與用戶進(jìn)程協(xié)作之同步阻塞方案(BIO) 深入理解Linux網(wǎng)絡(luò)——內(nèi)核與用戶進(jìn)程協(xié)作之多路復(fù)用方案(epoll) 深入理解Linux網(wǎng)絡(luò)——內(nèi)核是如何發(fā)送網(wǎng)絡(luò)包的 深入理解Linux網(wǎng)絡(luò)——本機(jī)網(wǎng)絡(luò)

    2024年02月13日
    瀏覽(21)
  • 深入理解C++內(nèi)存管理

    深入理解C++內(nèi)存管理

    C++的高抽象層次,又兼具高性能,是其他語(yǔ)言所無(wú)法替代的,C++標(biāo)準(zhǔn)保持穩(wěn)定發(fā)展,更加現(xiàn)代化,更加強(qiáng)大。但在各種活躍編程語(yǔ)言中,C++門(mén)檻依然很高,尤其C++的內(nèi)存問(wèn)題(內(nèi)存泄露,內(nèi)存溢出,內(nèi)存宕機(jī),堆棧破壞等問(wèn)題),需要理解C++標(biāo)準(zhǔn)對(duì)象模型,C++標(biāo)準(zhǔn)庫(kù),標(biāo)準(zhǔn)

    2023年04月08日
    瀏覽(28)
  • 【深入理解C】動(dòng)態(tài)內(nèi)存管理

    【深入理解C】動(dòng)態(tài)內(nèi)存管理

    大家在編寫(xiě)C程序的時(shí)候,是否會(huì)遇到數(shù)組空間不夠大,或者一次性就把空間開(kāi)大了, 又怕造成空間開(kāi)銷浪費(fèi)等內(nèi)存大小相關(guān)問(wèn)題不得其解的時(shí)候呢? 那么這一章內(nèi)容將會(huì)給你帶來(lái)解決這類問(wèn)題的好辦法—— 動(dòng)態(tài)內(nèi)存開(kāi)辟 為什么要?jiǎng)討B(tài)內(nèi)存分配 動(dòng)態(tài)內(nèi)存函數(shù)的介紹 malloc與

    2023年04月08日
    瀏覽(21)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包