精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Linux內(nèi)存分頁(yè)機(jī)制:解鎖高效內(nèi)存管理方式

系統(tǒng) Linux
在Linux操作系統(tǒng)復(fù)雜而精妙的架構(gòu)體系中,內(nèi)存管理堪稱其中的核心與關(guān)鍵。想象一下,系統(tǒng)如同一個(gè)繁忙的大型工廠,內(nèi)存則是工廠里至關(guān)重要的原材料倉(cāng)庫(kù),各個(gè)進(jìn)程就像不同的生產(chǎn)線,它們都對(duì)內(nèi)存資源有著迫切的需求。

在Linux操作系統(tǒng)復(fù)雜而精妙的架構(gòu)體系中,內(nèi)存管理堪稱其中的核心與關(guān)鍵。想象一下,系統(tǒng)如同一個(gè)繁忙的大型工廠,內(nèi)存則是工廠里至關(guān)重要的原材料倉(cāng)庫(kù),各個(gè)進(jìn)程就像不同的生產(chǎn)線,它們都對(duì)內(nèi)存資源有著迫切的需求。如何高效地管理這些內(nèi)存,讓每一條生產(chǎn)線都能順暢運(yùn)轉(zhuǎn),避免資源浪費(fèi)與沖突,成了決定系統(tǒng)整體性能的關(guān)鍵因素。

在早期,分段機(jī)制曾在操作系統(tǒng)的內(nèi)存管理領(lǐng)域占據(jù)主導(dǎo)地位。但隨著技術(shù)的飛速發(fā)展,新的挑戰(zhàn)不斷涌現(xiàn),內(nèi)存碎片問(wèn)題日益凸顯,就如同倉(cāng)庫(kù)中的原材料被零散放置,難以被高效取用,這嚴(yán)重影響了內(nèi)存的使用效率與系統(tǒng)性能。為了應(yīng)對(duì)這一難題,分頁(yè)機(jī)制應(yīng)運(yùn)而生,它如同一位聰明的倉(cāng)庫(kù)管理員,重新規(guī)劃了內(nèi)存的存儲(chǔ)方式,將內(nèi)存分割成固定大小的小片,也就是內(nèi)存頁(yè),以此來(lái)提升內(nèi)存的空間利用率,逐漸成為現(xiàn)代操作系統(tǒng)內(nèi)存管理的主流選擇。今天,就讓我們一同深入探索 Linux 內(nèi)存分頁(yè)機(jī)制的奧秘,解鎖這一高效內(nèi)存管理方式背后的關(guān)鍵技術(shù) 。

一、內(nèi)存分頁(yè)機(jī)制

在 Linux 的世界里,內(nèi)存分頁(yè)機(jī)制就像是一位有條不紊的大管家,精心管理著系統(tǒng)的內(nèi)存資源。簡(jiǎn)單來(lái)說(shuō),內(nèi)存分頁(yè)機(jī)制就是把物理內(nèi)存和虛擬內(nèi)存分割成固定大小的小塊,這些小塊被稱作 “頁(yè)” ,每個(gè)頁(yè)的大小一般為 4KB 或者 8KB。就好比你有一個(gè)巨大的倉(cāng)庫(kù)(內(nèi)存),為了更好地管理里面的貨物(數(shù)據(jù)),你把倉(cāng)庫(kù)劃分成了一個(gè)個(gè)大小相同的小隔間(頁(yè))。

1.1什么是分頁(yè)機(jī)制

分頁(yè)機(jī)制是 80x86 內(nèi)存管理機(jī)制的第二部分。它在分段機(jī)制的基礎(chǔ)上完成虛擬地址到物理地址的轉(zhuǎn)換過(guò)程。分段機(jī)制把邏輯地址轉(zhuǎn)換成線性地址,而分頁(yè)機(jī)制則把線性地址轉(zhuǎn)換成物理地址。分頁(yè)機(jī)制可用于任何一種分段模型。處理器分頁(yè)機(jī)制會(huì)把線性地址空間劃分成頁(yè)面,然后這些線性地址空間頁(yè)面被映射到物理地址空間的頁(yè)面上。分頁(yè)機(jī)制的幾種頁(yè)面級(jí)保護(hù)措施,可和分段機(jī)制保護(hù)措施或用或替代分段機(jī)制的保護(hù)措施。

1.2分頁(yè)機(jī)制如何啟用

在我們進(jìn)行程序開(kāi)發(fā)的時(shí)候,一般情況下,是不需要管理內(nèi)存的,也不需要操心內(nèi)存夠不夠用,其實(shí),這就是分頁(yè)機(jī)制給我們帶來(lái)的好處。它是實(shí)現(xiàn)虛擬存儲(chǔ)的關(guān)鍵,位于線性地址與物理地址之間,在使用這種內(nèi)存分頁(yè)管理方法時(shí),每個(gè)執(zhí)行中的進(jìn)程(任務(wù))可以使用比實(shí)際內(nèi)存容量大得多的連續(xù)地址空間。而且當(dāng)系統(tǒng)內(nèi)存實(shí)際上被分成很多凌亂的塊時(shí),它可以建立一個(gè)大而連續(xù)的內(nèi)存空間的映象,好讓程序不用操心和管理這些分散的內(nèi)存塊。分頁(yè)機(jī)制增強(qiáng)了分段機(jī)制的性能。頁(yè)地址變換是建立在段變換基礎(chǔ)之上的。因?yàn)椋喂芾頇C(jī)制對(duì)于Intel處理器來(lái)說(shuō)是最基本的,任何時(shí)候都無(wú)法關(guān)閉。所以即使啟用了頁(yè)管理功能,分段機(jī)制依然是起作用的,段部件也依然工作。

分頁(yè)只能在保護(hù)模式(CR0.PE = 1)下使用。在保護(hù)模式下,是否開(kāi)啟分頁(yè),由 CR0. PG 位(位 31)決定:

  • 當(dāng) CR0.PG = 0 時(shí),未開(kāi)啟分頁(yè),線性地址等同于物理地址;
  • 當(dāng) CR0.PG = 1 時(shí),開(kāi)啟分頁(yè)。

1.3分頁(yè)機(jī)制線性地址到物理地址轉(zhuǎn)換過(guò)程

80x86使用 4K 字節(jié)固定大小的頁(yè)面,每個(gè)頁(yè)面均是 4KB,并且對(duì)其于 4K 地址邊界處。這表示分頁(yè)機(jī)制把 2^32字節(jié)(4GB)的線性地址空間劃分成 2^20(1M = 1048576)個(gè)頁(yè)面。分頁(yè)機(jī)制通過(guò)把線性地址空間中的頁(yè)面重新定位到物理地址空間中進(jìn)行操作。由于 4K 大小的頁(yè)面作為一個(gè)單元進(jìn)行映射,并且對(duì)其于 4K 邊界,因此線性地址的低 12 位可做為頁(yè)內(nèi)偏移地量直接作為物理地址的低 12 位。分頁(yè)機(jī)制執(zhí)行的重定向功能可以看作是把線性地址的高 20 位轉(zhuǎn)換到對(duì)應(yīng)物理地址的高 20 位。

線性到物理地址轉(zhuǎn)換功能,被擴(kuò)展成允許一個(gè)線性地址被標(biāo)注為無(wú)效的,而非要讓其產(chǎn)生一個(gè)物理地址。以下兩種情況一個(gè)頁(yè)面可以被標(biāo)注為無(wú)效的:

1. 操作系統(tǒng)不支持的線性地址。

2. 對(duì)應(yīng)的虛擬內(nèi)存系統(tǒng)中的頁(yè)面在磁盤(pán)上而非在物理內(nèi)存中。

在第一中情況下,產(chǎn)生無(wú)效地址的程序必須被終止,在第二種情況下,該無(wú)效地址實(shí)際上是請(qǐng)求 操作系統(tǒng)虛擬內(nèi)存管理器 把對(duì)應(yīng)的頁(yè)面從磁盤(pán)加載到物理內(nèi)存中,以供程序訪問(wèn)。因?yàn)闊o(wú)效頁(yè)面通常與虛擬存儲(chǔ)系統(tǒng)相關(guān),因此它們被稱為不存在頁(yè)面,由頁(yè)表中稱為存在的屬性來(lái)確定。

當(dāng)使用分頁(yè)時(shí),處理器會(huì)把線性地址空間劃分成固定大小的頁(yè)面(4KB),這些頁(yè)面可以映射到物理內(nèi)存中或磁盤(pán)存儲(chǔ)空間中,當(dāng)一個(gè)程序引用內(nèi)存中的邏輯地址時(shí),處理器會(huì)把該邏輯地址轉(zhuǎn)換成一個(gè)線性地址,然后使用分頁(yè)機(jī)制把該線性地址轉(zhuǎn)換成對(duì)應(yīng)的物理地址。

如果包含線性地址的頁(yè)面不在當(dāng)前物理內(nèi)存中,處理器就會(huì)產(chǎn)生一個(gè)頁(yè)錯(cuò)誤異常。頁(yè)錯(cuò)誤異常處理程序就會(huì)讓操作系統(tǒng)從磁盤(pán)中把相應(yīng)頁(yè)面加載到物理內(nèi)存中(操作過(guò)程中可能會(huì)把物理內(nèi)存中不同的頁(yè)面寫(xiě)到磁盤(pán)上)。當(dāng)頁(yè)面加載到物理內(nèi)存之后,從異常處理過(guò)程的返回操作會(huì)使異常的指令被重新執(zhí)行。處理器把用于線性地址轉(zhuǎn)換成物理地址和用于產(chǎn)生頁(yè)錯(cuò)誤的信息包含在存儲(chǔ)與內(nèi)存中的頁(yè)目錄與頁(yè)表中。

1.4分頁(yè)機(jī)制與分段機(jī)制的不同

分頁(yè)與分段的最大的不同之處在于分頁(yè)使用了固定長(zhǎng)度的頁(yè)面。段的長(zhǎng)度通常與存放在其中的代碼或數(shù)據(jù)結(jié)構(gòu)有相同的長(zhǎng)度。與段不同,頁(yè)面有固定的長(zhǎng)度。如果僅使用分段地址轉(zhuǎn)換,那么存儲(chǔ)在物理內(nèi)存中的一個(gè)數(shù)據(jù)結(jié)構(gòu)將包含其所有的部分。如果使用了分頁(yè),那么一個(gè)數(shù)據(jù)結(jié)構(gòu)就可以一部分存儲(chǔ)與物理內(nèi)存中,而另一部分保存在磁盤(pán)中。

為了減少地址轉(zhuǎn)換所要求的總線周期數(shù)量,最近訪問(wèn)的頁(yè)目錄和頁(yè)表會(huì)被存放在處理器的一個(gè)叫做轉(zhuǎn)換查找緩沖區(qū)(TLB)的緩沖器件中。TLB 可以滿足大多數(shù)讀頁(yè)目錄和頁(yè)表的請(qǐng)求而無(wú)需使用總線周期。只有當(dāng) TLB 中不包含所要求的頁(yè)表項(xiàng)是才會(huì)出現(xiàn)使用額外的總線周期從內(nèi)存讀取頁(yè)表項(xiàng)。通常在一個(gè)頁(yè)表項(xiàng)很長(zhǎng)時(shí)間沒(méi)有訪問(wèn)過(guò)時(shí)才會(huì)出現(xiàn)這種情況。

二、分頁(yè)機(jī)制類型

2.1四級(jí)分頁(yè)機(jī)制

前面我們提到Linux內(nèi)核僅使用了較少的分段機(jī)制,但是卻對(duì)分頁(yè)機(jī)制的依賴性很強(qiáng),其使用一種適合32位和64位結(jié)構(gòu)的通用分頁(yè)模型,該模型使用四級(jí)分頁(yè)機(jī)制,即

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

頁(yè)全局目錄包含若干頁(yè)上級(jí)目錄的地址:

  • 頁(yè)上級(jí)目錄又依次包含若干頁(yè)中間目錄的地址;
  • 而頁(yè)中間目錄又包含若干頁(yè)表的地址;
  • 每一個(gè)頁(yè)表項(xiàng)指向一個(gè)頁(yè)框。

因此線性地址因此被分成五個(gè)部分,而每一部分的大小與具體的計(jì)算機(jī)體系結(jié)構(gòu)有關(guān)。

2.2不同架構(gòu)的分頁(yè)機(jī)制

對(duì)于不同的體系結(jié)構(gòu),Linux采用的四級(jí)頁(yè)表目錄的大小有所不同:對(duì)于i386而言,僅采用二級(jí)頁(yè)表,即頁(yè)上層目錄和頁(yè)中層目錄長(zhǎng)度為0;對(duì)于啟用PAE的i386,采用了三級(jí)頁(yè)表,即頁(yè)上層目錄長(zhǎng)度為0;對(duì)于64位體系結(jié)構(gòu),可以采用三級(jí)或四級(jí)頁(yè)表,具體選擇由硬件決定。

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

啟用了物理地址擴(kuò)展的32 位系統(tǒng)使用了三級(jí)頁(yè)表。Linux 的頁(yè)全局目錄對(duì)應(yīng)80x86 的頁(yè)目錄指針表(PDPT),取消了頁(yè)上級(jí)目錄,頁(yè)中間目錄對(duì)應(yīng)80x86的頁(yè)目錄,Linux的頁(yè)表對(duì)應(yīng)80x86的頁(yè)表。

最終,64位系統(tǒng)使用三級(jí)還是四級(jí)分頁(yè)取決于硬件對(duì)線性地址的位的劃分。

為什么linux熱衷:分頁(yè)>分段

那么,為什么Linux是如此地?zé)嶂允褂梅猪?yè)技術(shù)而對(duì)分段機(jī)制表現(xiàn)得那么地冷淡呢,因?yàn)長(zhǎng)inux的進(jìn)程處理很大程度上依賴于分頁(yè)。事實(shí)上,線性地址到物理地址的自動(dòng)轉(zhuǎn)換使下面的設(shè)計(jì)目標(biāo)變得可行:

給每一個(gè)進(jìn)程分配一塊不同的物理地址空間,這確保了可以有效地防止尋址錯(cuò)誤。

區(qū)別頁(yè)(即一組數(shù)據(jù))和頁(yè)框(即主存中的物理地址)之不同。這就允許存放在某個(gè)頁(yè)框中的一個(gè)頁(yè),然后保存到磁盤(pán)上,以后重新裝入這同一頁(yè)時(shí)又被裝在不同的頁(yè)框中。這就是虛擬內(nèi)存機(jī)制的基本要素。

每一個(gè)進(jìn)程有它自己的頁(yè)全局目錄和自己的頁(yè)表集。當(dāng)發(fā)生進(jìn)程切換時(shí),Linux把cr3控制寄存器的內(nèi)容保存在前一個(gè)執(zhí)行進(jìn)程的描述符中,然后把下一個(gè)要執(zhí)行進(jìn)程的描述符的值裝入cr3寄存器中。因此,當(dāng)新進(jìn)程重新開(kāi)始在CPU上執(zhí)行時(shí),分頁(yè)單元指向一組正確的頁(yè)表。

把線性地址映射到物理地址雖然有點(diǎn)復(fù)雜,但現(xiàn)在已經(jīng)成了一種機(jī)械式的任務(wù)。

三、分頁(yè)機(jī)制的工作原理

3.1分頁(yè)技術(shù)核心思想

分頁(yè)技術(shù)的核心思想,是把虛擬地址空間和物理內(nèi)存都劃分成固定大小的頁(yè),然后通過(guò)頁(yè)表來(lái)建立虛擬頁(yè)到物理頁(yè)框的映射關(guān)系。打個(gè)比方,你有一本厚厚的字典(虛擬地址空間),為了快速找到某個(gè)字(數(shù)據(jù)),你給字典的每一頁(yè)(虛擬頁(yè))都編了號(hào),同時(shí)在另一張紙上(頁(yè)表)記錄了每個(gè)編號(hào)對(duì)應(yīng)的實(shí)際頁(yè)碼(物理頁(yè)框)。

以 x86_64 架構(gòu)的 4 級(jí)頁(yè)表結(jié)構(gòu)為例,虛擬地址被分成了多個(gè)部分 。其中,[47-39] 位表示頁(yè)全局目錄(PGD),[38-30] 位表示上層頁(yè)目錄(PUD),[29-21] 位表示中間頁(yè)目錄(PMD),[20-12] 位表示頁(yè)表項(xiàng)(PTE),最后的 [11-0] 位則是頁(yè)內(nèi)偏移 。CPU 在訪問(wèn)內(nèi)存時(shí),會(huì)先通過(guò) CR3 寄存器找到頁(yè)全局目錄,然后根據(jù)虛擬地址中的 PGD 部分找到對(duì)應(yīng)的上層頁(yè)目錄,依此類推,逐級(jí)查詢,最終找到物理頁(yè)框號(hào),再結(jié)合頁(yè)內(nèi)偏移,就能得到準(zhǔn)確的物理地址。這個(gè)過(guò)程就像是你按照索引在多層書(shū)架上找一本書(shū),每一層索引都能幫你縮小查找范圍,最終準(zhǔn)確找到目標(biāo)書(shū)籍。

3.2多級(jí)頁(yè)表設(shè)計(jì)動(dòng)機(jī)與優(yōu)勢(shì)

在早期的內(nèi)存管理中,曾采用過(guò)單級(jí)頁(yè)表,即通過(guò)一個(gè)頁(yè)表直接將虛擬地址映射到物理地址。然而,這種方式存在明顯的弊端。隨著虛擬地址空間的不斷增大,例如在 64 位系統(tǒng)中,若使用單級(jí)頁(yè)表來(lái)映射所有虛擬地址,所需的頁(yè)表空間將變得極為龐大。以 48 位地址空間為例,若每個(gè)頁(yè)表項(xiàng)大小為 8 字節(jié),計(jì)算可知單級(jí)頁(yè)表需要 256TB 的內(nèi)存空間,這在實(shí)際應(yīng)用中幾乎是不可行的,不僅會(huì)占用大量的內(nèi)存資源,而且管理和維護(hù)如此巨大的頁(yè)表也會(huì)面臨諸多困難。

為了解決單級(jí)頁(yè)表的空間占用問(wèn)題,多級(jí)頁(yè)表應(yīng)運(yùn)而生。多級(jí)頁(yè)表的設(shè)計(jì)采用了稀疏存儲(chǔ)的策略,它并不是為整個(gè)虛擬地址空間都分配頁(yè)表,而是僅在需要時(shí)才分配實(shí)際使用的頁(yè)表項(xiàng)。這樣一來(lái),大大節(jié)省了內(nèi)存空間。

以 Linux 的四級(jí)頁(yè)表為例,當(dāng)一個(gè)進(jìn)程只使用 1GB 內(nèi)存時(shí),若采用四級(jí)頁(yè)表,其總大小約為 (1GB/2MB × 4 × 8B = 16KB) 。這是因?yàn)樵谒募?jí)頁(yè)表結(jié)構(gòu)中,每一級(jí)頁(yè)表都起到了篩選和定位的作用。當(dāng)進(jìn)程訪問(wèn)某個(gè)虛擬地址時(shí),首先通過(guò) CR3 寄存器找到頁(yè)全局目錄(PGD),PGD 根據(jù)虛擬地址中的相應(yīng)部分,找到上層頁(yè)目錄(PUD),若該 PUD 對(duì)應(yīng)的頁(yè)表項(xiàng)存在且有效,則繼續(xù)通過(guò) PUD 找到中間頁(yè)目錄(PMD),依此類推。如果在某一級(jí)發(fā)現(xiàn)對(duì)應(yīng)的頁(yè)表項(xiàng)不存在,說(shuō)明該虛擬地址尚未被映射到物理內(nèi)存,系統(tǒng)會(huì)根據(jù)需要?jiǎng)討B(tài)分配頁(yè)表項(xiàng),而不是預(yù)先分配整個(gè)頁(yè)表。

這種按需分配的方式,使得頁(yè)表僅占用實(shí)際使用的內(nèi)存區(qū)域,避免了為未使用的虛擬地址空間分配頁(yè)表所造成的內(nèi)存浪費(fèi) 。同時(shí),多級(jí)頁(yè)表的結(jié)構(gòu)也使得內(nèi)存管理更加靈活和高效,能夠更好地適應(yīng)不同進(jìn)程對(duì)內(nèi)存的需求。

四、linux中頁(yè)表處理數(shù)據(jù)結(jié)構(gòu)

分頁(yè)轉(zhuǎn)換功能由駐留在內(nèi)存中的表來(lái)描述,該表稱為頁(yè)表,存放在物理地址空間中。頁(yè)表可以看作是簡(jiǎn)單的 2^20 物理地址數(shù)組。線性到物理地址的映射功能可以簡(jiǎn)單地看作進(jìn)行數(shù)組查找。線性地址的高 20 位構(gòu)成這個(gè)數(shù)組的索引值,用于選擇對(duì)應(yīng)頁(yè)面的物理(基)地址。線性地址的低 12 位給出了頁(yè)面中的偏移量,加上頁(yè)面的基地址最終形成對(duì)應(yīng)的物理地址。由于頁(yè)面基地址對(duì)齊在 4K 邊界上,因此頁(yè)面基地址的低 12 為肯定是 0 ,這意味著 高 20 位的頁(yè)面基地址 和 12 位偏移地址連接組合在一起就能得到對(duì)應(yīng)的物理地址。

頁(yè)表中每個(gè)頁(yè)表項(xiàng) 大小為 32 位,由于只需其中的 20 位來(lái)存放頁(yè)面的物理基地址,因此剩下的 12 位可用于存放諸如頁(yè)面是否存在等的屬性信息。如果線性地址索引的頁(yè)表被標(biāo)注為存在,則表示該項(xiàng)有效, 我們可以從中取得頁(yè)面的物理地址。如果項(xiàng)中表明不存在,那么當(dāng)當(dāng)訪問(wèn)對(duì)應(yīng)物理界面時(shí)就會(huì)產(chǎn)生一個(gè)異常。

4.1 頁(yè)表類型定義

(1)pgd_t、pmd_t、pud_t和pte_t

Linux分別采用pgd_t、pmd_t、pud_t和pte_t四種數(shù)據(jù)結(jié)構(gòu)來(lái)表示頁(yè)全局目錄項(xiàng)、頁(yè)上級(jí)目錄項(xiàng)、頁(yè)中間目錄項(xiàng)和頁(yè)表項(xiàng)。這四種 數(shù)據(jù)結(jié)構(gòu)本質(zhì)上都是無(wú)符號(hào)長(zhǎng)整型unsigned long

Linux為了更嚴(yán)格數(shù)據(jù)類型檢查,將無(wú)符號(hào)長(zhǎng)整型unsigned long分別封裝成四種不同的頁(yè)表項(xiàng)。如果不采用這種方法,那么一個(gè)無(wú)符號(hào)長(zhǎng)整型數(shù)據(jù)可以傳入任何一個(gè)與四種頁(yè)表相關(guān)的函數(shù)或宏中,這將大大降低程序的健壯性。

pgprot_t是另一個(gè)64位(PAE激活時(shí))或32位(PAE禁用時(shí))的數(shù)據(jù)類型,它表示與一個(gè)單獨(dú)表項(xiàng)相關(guān)的保護(hù)標(biāo)志。首先我們查看一下子這些類型是如何定義的:

①pteval_t,pmdval_t,pudval_t,pgdval_t

參照arch/x86/include/asm/pgtable_64_types.h

#ifndef __ASSEMBLY__
#include <linux/types.h>

/*
 * These are used to make use of C type-checking..
 */
typedef unsigned long   pteval_t;
typedef unsigned long   pmdval_t;
typedef unsigned long   pudval_t;
typedef unsigned long   pgdval_t;
typedef unsigned long   pgprotval_t;

typedef struct { pteval_t pte; } pte_t;

#endif  /* !__ASSEMBLY__ */


②pgd_t、pmd_t、pud_t和pte_t

參照 /arch/x86/include/asm/pgtable_types.h

typedef struct { pgdval_t pgd; } pgd_t;

static inline pgd_t native_make_pgd(pgdval_t val)
{
        return (pgd_t) { val };
}

static inline pgdval_t native_pgd_val(pgd_t pgd)
{
        return pgd.pgd;
}

static inline pgdval_t pgd_flags(pgd_t pgd)
{
        return native_pgd_val(pgd) & PTE_FLAGS_MASK;
}

#if CONFIG_PGTABLE_LEVELS > 3
typedef struct { pudval_t pud; } pud_t;

static inline pud_t native_make_pud(pmdval_t val)
{
        return (pud_t) { val };
}

static inline pudval_t native_pud_val(pud_t pud)
{
        return pud.pud;
}
#else
#include <asm-generic/pgtable-nopud.h>

static inline pudval_t native_pud_val(pud_t pud)
{
        return native_pgd_val(pud.pgd);
}
#endif

#if CONFIG_PGTABLE_LEVELS > 2
typedef struct { pmdval_t pmd; } pmd_t;

static inline pmd_t native_make_pmd(pmdval_t val)
{
        return (pmd_t) { val };
}

static inline pmdval_t native_pmd_val(pmd_t pmd)
{
        return pmd.pmd;
}
#else
#include <asm-generic/pgtable-nopmd.h>

static inline pmdval_t native_pmd_val(pmd_t pmd)
{
        return native_pgd_val(pmd.pud.pgd);
}
#endif

static inline pudval_t pud_pfn_mask(pud_t pud)
{
        if (native_pud_val(pud) & _PAGE_PSE)
                return PHYSICAL_PUD_PAGE_MASK;
        else
                return PTE_PFN_MASK;
}

static inline pudval_t pud_flags_mask(pud_t pud)
{
        return ~pud_pfn_mask(pud);
}

static inline pudval_t pud_flags(pud_t pud)
{
        return native_pud_val(pud) & pud_flags_mask(pud);
}

static inline pmdval_t pmd_pfn_mask(pmd_t pmd)
{
        if (native_pmd_val(pmd) & _PAGE_PSE)
                return PHYSICAL_PMD_PAGE_MASK;
        else
                return PTE_PFN_MASK;
}

static inline pmdval_t pmd_flags_mask(pmd_t pmd)
{
        return ~pmd_pfn_mask(pmd);
}

static inline pmdval_t pmd_flags(pmd_t pmd)
{
        return native_pmd_val(pmd) & pmd_flags_mask(pmd);
}

static inline pte_t native_make_pte(pteval_t val)
{
        return (pte_t) { .pte = val };
}

static inline pteval_t native_pte_val(pte_t pte)
{
        return pte.pte;
}

static inline pteval_t pte_flags(pte_t pte)
{
        return native_pte_val(pte) & PTE_FLAGS_MASK;
}

③xxx_val和__xxx

參照/arch/x86/include/asm/pgtable.h

五個(gè)類型轉(zhuǎn)換宏(_ pte、_ pmd、_ pud、_ pgd和__ pgprot)把一個(gè)無(wú)符號(hào)整數(shù)轉(zhuǎn)換成所需的類型。

另外的五個(gè)類型轉(zhuǎn)換宏(pte_val,pmd_val, pud_val, pgd_val和pgprot_val)執(zhí)行相反的轉(zhuǎn)換,即把上面提到的四種特殊的類型轉(zhuǎn)換成一個(gè)無(wú)符號(hào)整數(shù)。

#define pgd_val(x)      native_pgd_val(x)
#define __pgd(x)        native_make_pgd(x)

#ifndef __PAGETABLE_PUD_FOLDED
#define pud_val(x)      native_pud_val(x)
#define __pud(x)        native_make_pud(x)
#endif

#ifndef __PAGETABLE_PMD_FOLDED
#define pmd_val(x)      native_pmd_val(x)
#define __pmd(x)        native_make_pmd(x)
#endif

#define pte_val(x)      native_pte_val(x)
#define __pte(x)        native_make_pte(x)

這里需要區(qū)別指向頁(yè)表項(xiàng)的指針和頁(yè)表項(xiàng)所代表的數(shù)據(jù)。以pgd_t類型為例子,如果已知一個(gè)pgd_t類型的指針pgd,那么通過(guò)pgd_val(*pgd)即可獲得該頁(yè)表項(xiàng)(也就是一個(gè)無(wú)符號(hào)長(zhǎng)整型數(shù)據(jù)),這里利用了面向?qū)ο蟮乃枷搿?/span>

4.2頁(yè)表描述宏

參照arch/x86/include/asm/pgtable_64

linux中使用下列宏簡(jiǎn)化了頁(yè)表處理,對(duì)于每一級(jí)頁(yè)表都使用有以下三個(gè)關(guān)鍵描述宏:

宏字段

描述

XXX_SHIFT

指定Offset字段的位數(shù)

XXX_SIZE

頁(yè)的大小

XXX_MASK

用以屏蔽Offset字段的所有位

我們的四級(jí)頁(yè)表,對(duì)應(yīng)的宏分別由PAGE,PMD,PUD,PGDIR

宏字段前綴

描述

PGDIR

頁(yè)全局目錄(Page Global Directory)

PUD

頁(yè)上級(jí)目錄(Page Upper Directory)

PMD

頁(yè)中間目錄(Page Middle Directory)

PAGE

頁(yè)表(Page Table)

PAGE宏–頁(yè)表(Page Table)

字段

描述

PAGE_MASK

用以屏蔽Offset字段的所有位

PAGE_SHIFT

指定Offset字段的位數(shù)

PAGE_SIZE

頁(yè)的大小

定義如下,在/arch/x86/include/asm/page_types.h文件中

/* PAGE_SHIFT determines the page size */
 #define PAGE_SHIFT      12
 #define PAGE_SIZE       (_AC(1,UL) << PAGE_SHIFT)
 #define PAGE_MASK       (~(PAGE_SIZE-1))

當(dāng)用于80x86處理器時(shí),PAGE_SHIFT返回的值為12,由于頁(yè)內(nèi)所有地址都必須放在Offset字段, 因此80x86系統(tǒng)的頁(yè)的大小PAGE_SIZE是2^12=4096字節(jié),PAGE_MASK宏產(chǎn)生的值為0xfffff000,用以屏蔽Offset字段的所有位。

PMD-Page Middle Directory (頁(yè)目錄)

字段

描述

PMD_SHIFT

指定線性地址的Offset和Table字段的總位數(shù);換句話說(shuō),是頁(yè)中間目錄項(xiàng)可以映射的區(qū)域大小的對(duì)數(shù)

PMD_SIZE

用于計(jì)算由頁(yè)中間目錄的一個(gè)單獨(dú)表項(xiàng)所映射的區(qū)域大小,也就是一個(gè)頁(yè)表的大小

PMD_MASK

用于屏蔽Offset字段與Table字段的所有位

當(dāng)PAE 被禁用時(shí),PMD_SHIFT 產(chǎn)生的值為22(來(lái)自O(shè)ffset 的12 位加上來(lái)自Table 的10 位),PMD_SIZE 產(chǎn)生的值為222 或 4 MB,PMD_MASK產(chǎn)生的值為 0xffc00000。

相反,當(dāng)PAE被激活時(shí),PMD_SHIFT 產(chǎn)生的值為21 (來(lái)自O(shè)ffset的12位加上來(lái)自Table的9位),PMD_SIZE 產(chǎn)生的值為2^21 或2 MB PMD_MASK產(chǎn)生的值為 0xffe00000。

大型頁(yè)不使用最后一級(jí)頁(yè)表,所以產(chǎn)生大型頁(yè)尺寸的LARGE_PAGE_SIZE 宏等于PMD_SIZE(2PMD_SHIFT),而在大型頁(yè)地址中用于屏蔽Offset字段和Table字段的所有位的LARGE_PAGE_MASK宏,就等于PMD_MASK。

PUD_SHIFT-頁(yè)上級(jí)目錄(Page Upper Directory)

字段

描述

PUD_SHIFT

確定頁(yè)上級(jí)目錄項(xiàng)能映射的區(qū)域大小的位數(shù)

PUD_SIZE

用于計(jì)算頁(yè)全局目錄中的一個(gè)單獨(dú)表項(xiàng)所能映射的區(qū)域大小。

PUD_MASK

用于屏蔽Offset字段,Table字段,Middle Air字段和Upper Air字段的所有位

在80x86處理器上,PUD_SHIFT總是等價(jià)于PMD_SHIFT,而PUD_SIZE則等于4MB或2MB。

PGDIR_SHIFT-頁(yè)全局目錄(Page Global Directory)

字段

描述

PGDIR_SHIFT

確定頁(yè)全局頁(yè)目錄項(xiàng)能映射的區(qū)域大小的位數(shù)

PGDIR_SIZE

用于計(jì)算頁(yè)全局目錄中一個(gè)單獨(dú)表項(xiàng)所能映射區(qū)域的大小

PGDIR_MASK

用于屏蔽Offset, Table,Middle Air及Upper Air的所有位

當(dāng)PAE 被禁止時(shí),PGDIR_SHIFT 產(chǎn)生的值為22(與PMD_SHIFT 和PUD_SHIFT 產(chǎn)生的值相同),PGDIR_SIZE 產(chǎn)生的值為 222 或 4 MB,PGDIR_MASK 產(chǎn)生的值為 0xffc00000。

相反,當(dāng)PAE被激活時(shí),PGDIR_SHIFT 產(chǎn)生的值為30 (12 位Offset 加 9 位Table再加 9位 Middle Air),PGDIR_SIZE 產(chǎn)生的值為230 或 1 GBPGDIR_MASK產(chǎn)生的值為0xc0000000

PTRS_PER_PTE, PTRS_PER_PMD, PTRS_PER_PUD以及PTRS_PER_PGD用于計(jì)算頁(yè)表、頁(yè)中間目錄、頁(yè)上級(jí)目錄和頁(yè)全局目錄表中表項(xiàng)的個(gè)數(shù)。當(dāng)PAE被禁止時(shí),它們產(chǎn)生的值分別為1024,1,1和1024。當(dāng)PAE被激活時(shí),產(chǎn)生的值分別為512,512,1和4。

4.3頁(yè)表處理函數(shù)

內(nèi)核還提供了許多宏和函數(shù)用于讀或修改頁(yè)表表項(xiàng):

  • 如果相應(yīng)的表項(xiàng)值為0,那么,宏pte_none、pmd_none、pud_none和 pgd_none產(chǎn)生的值為1,否則產(chǎn)生的值為0。
  • 宏pte_clear、pmd_clear、pud_clear和 pgd_clear清除相應(yīng)頁(yè)表的一個(gè)表項(xiàng),由此禁止進(jìn)程使用由該頁(yè)表項(xiàng)映射的線性地址。ptep_get_and_clear( )函數(shù)清除一個(gè)頁(yè)表項(xiàng)并返回前一個(gè)值。
  • set_pte,set_pmd,set_pud和set_pgd向一個(gè)頁(yè)表項(xiàng)中寫(xiě)入指定的值。set_pte_atomic與set_pte作用相同,但是當(dāng)PAE被激活時(shí)它同樣能保證64位的值能被原子地寫(xiě)入。
  • 如果a和b兩個(gè)頁(yè)表項(xiàng)指向同一頁(yè)并且指定相同訪問(wèn)優(yōu)先級(jí),pte_same(a,b)返回1,否則返回0。
  • 如果頁(yè)中間目錄項(xiàng)指向一個(gè)大型頁(yè)(2MB或4MB),pmd_large(e)返回1,否則返回0。

宏pmd_bad由函數(shù)使用并通過(guò)輸入?yún)?shù)傳遞來(lái)檢查頁(yè)中間目錄項(xiàng)。如果目錄項(xiàng)指向一個(gè)不能使用的頁(yè)表,也就是說(shuō),如果至少出現(xiàn)以下條件中的一個(gè),則這個(gè)宏產(chǎn)生的值為1:

  • 頁(yè)不在主存中(Present標(biāo)志被清除)。
  • 頁(yè)只允許讀訪問(wèn)(Read/Write標(biāo)志被清除)。
  • Acessed或者Dirty位被清除(對(duì)于每個(gè)現(xiàn)有的頁(yè)表,Linux總是強(qiáng)制設(shè)置這些標(biāo)志)。

pud_bad宏和pgd_bad宏總是產(chǎn)生0。沒(méi)有定義pte_bad宏,因?yàn)轫?yè)表項(xiàng)引用一個(gè)不在主存中的頁(yè),一個(gè)不可寫(xiě)的頁(yè)或一個(gè)根本無(wú)法訪問(wèn)的頁(yè)都是合法的。

如果一個(gè)頁(yè)表項(xiàng)的Present標(biāo)志或者Page Size標(biāo)志等于1,則pte_present宏產(chǎn)生的值為1,否則為0。

前面講過(guò)頁(yè)表項(xiàng)的Page Size標(biāo)志對(duì)微處理器的分頁(yè)部件來(lái)講沒(méi)有意義,然而,對(duì)于當(dāng)前在主存中卻又沒(méi)有讀、寫(xiě)或執(zhí)行權(quán)限的頁(yè),內(nèi)核將其Present和Page Size分別標(biāo)記為0和1。

這樣,任何試圖對(duì)此類頁(yè)的訪問(wèn)都會(huì)引起一個(gè)缺頁(yè)異常,因?yàn)轫?yè)的Present標(biāo)志被清0,而內(nèi)核可以通過(guò)檢查Page Size的值來(lái)檢測(cè)到產(chǎn)生異常并不是因?yàn)槿表?yè)。

如果相應(yīng)表項(xiàng)的Present標(biāo)志等于1,也就是說(shuō),如果對(duì)應(yīng)的頁(yè)或頁(yè)表被裝載入主存,pmd_present宏產(chǎn)生的值為1。pud_present宏和pgd_present宏產(chǎn)生的值總是1。

(1)查詢頁(yè)表項(xiàng)中任意一個(gè)標(biāo)志的當(dāng)前值

下表中列出的函數(shù)用來(lái)查詢頁(yè)表項(xiàng)中任意一個(gè)標(biāo)志的當(dāng)前值;除了pte_file()外,其他函數(shù)只有在pte_present返回1的時(shí)候,才能正常返回頁(yè)表項(xiàng)中任意一個(gè)標(biāo)志。

  • pte_user( ):讀 User/Supervisor 標(biāo)志
  • pte_read( ):讀 User/Supervisor 標(biāo)志(表示 80x86 處理器上的頁(yè)不受讀的保護(hù))
  • pte_write( ):讀 Read/Write 標(biāo)志
  • pte_exec( ):讀 User/Supervisor 標(biāo)志( 80x86 處理器上的頁(yè)不受代碼執(zhí)行的保護(hù))
  • pte_dirty( ):讀 Dirty 標(biāo)志
  • pte_young( ):讀 Accessed 標(biāo)志
  • pte_file( ):讀 Dirty 標(biāo)志(當(dāng) Present 標(biāo)志被清除而 Dirty 標(biāo)志被設(shè)置時(shí),頁(yè)屬于一個(gè)非線性磁盤(pán)文件映射)

(2)設(shè)置頁(yè)表項(xiàng)中各標(biāo)志的值

下表列出的另一組函數(shù)用于設(shè)置頁(yè)表項(xiàng)中各標(biāo)志的值

  • mk_pte_huge( ):設(shè)置頁(yè)表項(xiàng)中的 Page Size 和 Present 標(biāo)志
  • pte_wrprotect( ):清除 Read/Write 標(biāo)志
  • pte_rdprotect( ):清除 User/Supervisor 標(biāo)志
  • pte_exprotect( ):清除 User/Supervisor 標(biāo)志
  • pte_mkwrite( ):設(shè)置 Read/Write 標(biāo)志
  • pte_mkread( ):設(shè)置 User/Supervisor 標(biāo)志
  • pte_mkexec( ):設(shè)置 User/Supervisor 標(biāo)志
  • pte_mkclean( ):清除 Dirty 標(biāo)志
  • pte_mkdirty( ):設(shè)置 Dirty 標(biāo)志
  • pte_mkold( ):清除 Accessed 標(biāo)志(把此頁(yè)標(biāo)記為未訪問(wèn))
  • pte_mkyoung( ):設(shè)置 Accessed 標(biāo)志(把此頁(yè)標(biāo)記為訪問(wèn)過(guò))
  • pte_modify(p,v):把頁(yè)表項(xiàng) p 的所有訪問(wèn)權(quán)限設(shè)置為指定的值
  • ptep_set_wrprotect():與 pte_wrprotect( ) 類似,但作用于指向頁(yè)表項(xiàng)的指針
  • ptep_set_access_flags( ):如果 Dirty 標(biāo)志被設(shè)置為 1 則將頁(yè)的訪問(wèn)權(quán)設(shè)置為指定的值,并調(diào)用flush_tlb_page() 函數(shù)ptep_mkdirty()與 pte_mkdirty( ) 類似,但作用于指向頁(yè)表項(xiàng)的指針。
  • ptep_test_and_clear_dirty( ):與 pte_mkclean( ) 類似,但作用于指向頁(yè)表項(xiàng)的指針并返回 Dirty 標(biāo)志的舊值
  • ptep_test_and_clear_young( ):與 pte_mkold( ) 類似,但作用于指向頁(yè)表項(xiàng)的指針并返回 Accessed標(biāo)志的舊值

(3)宏函數(shù)-把一個(gè)頁(yè)地址和一組保護(hù)標(biāo)志組合成頁(yè)表項(xiàng),或者執(zhí)行相反的操作

現(xiàn)在,我們來(lái)討論下表中列出的宏,它們把一個(gè)頁(yè)地址和一組保護(hù)標(biāo)志組合成頁(yè)表項(xiàng),或者執(zhí)行相反的操作,從一個(gè)頁(yè)表項(xiàng)中提取出頁(yè)地址。請(qǐng)注意這其中的一些宏對(duì)頁(yè)的引用是通過(guò) “頁(yè)描述符”的線性地址,而不是通過(guò)該頁(yè)本身的線性地址。

  • pgd_index(addr):找到線性地址 addr 對(duì)應(yīng)的的目錄項(xiàng)在頁(yè)全局目錄中的索引(相對(duì)位置)
  • pgd_offset(mm, addr):接收內(nèi)存描述符地址 mm 和線性地址 addr 作為參數(shù)。這個(gè)宏產(chǎn)生地址addr 在頁(yè)全局目錄中相應(yīng)表項(xiàng)的線性地址;通過(guò)內(nèi)存描述符 mm 內(nèi)的一個(gè)指針可以找到這個(gè)頁(yè)全局目錄pgd_offset_k(addr)產(chǎn)生主內(nèi)核頁(yè)全局目錄中的某個(gè)項(xiàng)的線性地址,該項(xiàng)對(duì)應(yīng)于地址
  • addrpgd_page(pgd):通過(guò)頁(yè)全局目錄項(xiàng) pgd 產(chǎn)生頁(yè)上級(jí)目錄所在頁(yè)框的頁(yè)描述符地址。在兩級(jí)或三級(jí)分頁(yè)系統(tǒng)中,該宏等價(jià)于 pud_page() ,后者應(yīng)用于頁(yè)上級(jí)目錄項(xiàng)
  • pud_offset(pgd, addr):參數(shù)為指向頁(yè)全局目錄項(xiàng)的指針 pgd 和線性地址 addr 。這個(gè)宏產(chǎn)生頁(yè)上級(jí)目錄中目錄項(xiàng) addr 對(duì)應(yīng)的線性地址。在兩級(jí)或三級(jí)分頁(yè)系統(tǒng)中,該宏產(chǎn)生 pgd ,即一個(gè)頁(yè)全局目錄項(xiàng)的地址
  • pud_page(pud):通過(guò)頁(yè)上級(jí)目錄項(xiàng) pud 產(chǎn)生相應(yīng)的頁(yè)中間目錄的線性地址。在兩級(jí)分頁(yè)系統(tǒng)中,該宏等價(jià)于 pmd_page() ,后者應(yīng)用于頁(yè)中間目錄項(xiàng)
  • pmd_index(addr):產(chǎn)生線性地址 addr 在頁(yè)中間目錄中所對(duì)應(yīng)目錄項(xiàng)的索引(相對(duì)位置)
  • pmd_offset(pud, addr):接收指向頁(yè)上級(jí)目錄項(xiàng)的指針 pud 和線性地址 addr 作為參數(shù)。這個(gè)宏產(chǎn)生目錄項(xiàng) addr 在頁(yè)中間目錄中的偏移地址。在兩級(jí)或三級(jí)分頁(yè)系統(tǒng)中,它產(chǎn)生 pud ,即頁(yè)全局目錄項(xiàng)的地址
  • pmd_page(pmd):通過(guò)頁(yè)中間目錄項(xiàng) pmd 產(chǎn)生相應(yīng)頁(yè)表的頁(yè)描述符地址。在兩級(jí)或三級(jí)分頁(yè)系統(tǒng)中, pmd 實(shí)際上是頁(yè)全局目錄中的一項(xiàng)mk_pte(p,prot)接收頁(yè)描述符地址 p 和一組訪問(wèn)權(quán)限 prot 作為參數(shù),并創(chuàng)建相應(yīng)的頁(yè)表項(xiàng)
  • pte_index(addr):產(chǎn)生線性地址 addr 對(duì)應(yīng)的表項(xiàng)在頁(yè)表中的索引(相對(duì)位置)
  • pte_offset_kernel(dir,addr):線性地址 addr 在頁(yè)中間目錄 dir 中有一個(gè)對(duì)應(yīng)的項(xiàng),該宏就產(chǎn)生這個(gè)對(duì)應(yīng)項(xiàng),即頁(yè)表的線性地址。另外,該宏只在主內(nèi)核頁(yè)表上使用
  • pte_offset_map(dir, addr):接收指向一個(gè)頁(yè)中間目錄項(xiàng)的指針 dir 和線性地址 addr 作為參數(shù),它產(chǎn)生與線性地址 addr 相對(duì)應(yīng)的頁(yè)表項(xiàng)的線性地址。如果頁(yè)表被保存在高端存儲(chǔ)器中,那么內(nèi)核建立一個(gè)臨時(shí)內(nèi)核映射,并用 pte_unmap 對(duì)它進(jìn)行釋放。pte_offset_map_nested 宏和 pte_unmap_nested 宏是相同的,但它們使用不同的臨時(shí)內(nèi)核映射
  • pte_page( x ):返回頁(yè)表項(xiàng) x 所引用頁(yè)的描述符地址
  • pte_to_pgoff( pte ):從一個(gè)頁(yè)表項(xiàng)的 pte 字段內(nèi)容中提取出文件偏移量,這個(gè)偏移量對(duì)應(yīng)著一個(gè)非線性文件內(nèi)存映射所在的頁(yè)
  • pgoff_to_pte(offset ):為非線性文件內(nèi)存映射所在的頁(yè)創(chuàng)建對(duì)應(yīng)頁(yè)表項(xiàng)的內(nèi)容

(4)簡(jiǎn)化頁(yè)表項(xiàng)的創(chuàng)建和撤消

下面我們羅列最后一組函數(shù)來(lái)簡(jiǎn)化頁(yè)表項(xiàng)的創(chuàng)建和撤消。當(dāng)使用兩級(jí)頁(yè)表時(shí),創(chuàng)建或刪除一個(gè)頁(yè)中間目錄項(xiàng)是不重要的。如本節(jié)前部分所述,頁(yè)中間目錄僅含有一個(gè)指向下屬頁(yè)表的目錄項(xiàng)。所以,頁(yè)中間目錄項(xiàng)只是頁(yè)全局目錄中的一項(xiàng)而已。然而當(dāng)處理頁(yè)表時(shí),創(chuàng)建一個(gè)頁(yè)表項(xiàng)可能很復(fù)雜,因?yàn)榘?yè)表項(xiàng)的那個(gè)頁(yè)表可能就不存在。在這樣的情況下,有必要分配一個(gè)新頁(yè)框,把它填寫(xiě)為 0 ,并把這個(gè)表項(xiàng)加入。

如果 PAE 被激活,內(nèi)核使用三級(jí)頁(yè)表。當(dāng)內(nèi)核創(chuàng)建一個(gè)新的頁(yè)全局目錄時(shí),同時(shí)也分配四個(gè)相應(yīng)的頁(yè)中間目錄;只有當(dāng)父頁(yè)全局目錄被釋放時(shí),這四個(gè)頁(yè)中間目錄才得以釋放。當(dāng)使用兩級(jí)或三級(jí)分頁(yè)時(shí),頁(yè)上級(jí)目錄項(xiàng)總是被映射為頁(yè)全局目錄中的一個(gè)單獨(dú)項(xiàng)。與以往一樣,下表中列出的函數(shù)描述是針對(duì) 80x86 構(gòu)架的。

五、線性地址轉(zhuǎn)換

5.1分頁(yè)模式下的的線性地址轉(zhuǎn)換

線性地址、頁(yè)表和頁(yè)表項(xiàng)線性地址不管系統(tǒng)采用多少級(jí)分頁(yè)模型,線性地址本質(zhì)上都是索引+偏移量的形式,甚至你可以將整個(gè)線性地址看作N+1個(gè)索引的組合,N是系統(tǒng)采用的分頁(yè)級(jí)數(shù)。在四級(jí)分頁(yè)模型下,線性地址被分為5部分,如下圖:

在線性地址中,每個(gè)頁(yè)表索引即代表線性地址在對(duì)應(yīng)級(jí)別的頁(yè)表中中關(guān)聯(lián)的頁(yè)表項(xiàng)。正是這種索引與頁(yè)表項(xiàng)的對(duì)應(yīng)關(guān)系形成了整個(gè)頁(yè)表映射機(jī)制。

(1)頁(yè)表

多個(gè)頁(yè)表項(xiàng)的集合則為頁(yè)表,一個(gè)頁(yè)表內(nèi)的所有頁(yè)表項(xiàng)是連續(xù)存放的。頁(yè)表本質(zhì)上是一堆數(shù)據(jù),因此也是以頁(yè)為單位存放在主存中的。因此,在虛擬地址轉(zhuǎn)化物理物理地址的過(guò)程中,每訪問(wèn)一級(jí)頁(yè)表就會(huì)訪問(wèn)一次內(nèi)存。

(2)頁(yè)表項(xiàng)

頁(yè)表項(xiàng)從四種頁(yè)表項(xiàng)的數(shù)據(jù)結(jié)構(gòu)可以看出,每個(gè)頁(yè)表項(xiàng)其實(shí)就是一個(gè)無(wú)符號(hào)長(zhǎng)整型數(shù)據(jù)。每個(gè)頁(yè)表項(xiàng)分兩大類信息:頁(yè)框基地址和頁(yè)的屬性信息。在x86-32體系結(jié)構(gòu)中,每個(gè)頁(yè)表項(xiàng)的結(jié)構(gòu)圖如下:

這個(gè)圖是一個(gè)通用模型,其中頁(yè)表項(xiàng)的前20位是物理頁(yè)的基地址。由于32位的系統(tǒng)采用4kb大小的 頁(yè),因此每個(gè)頁(yè)表項(xiàng)的后12位均為0。內(nèi)核將后12位充分利用,每個(gè)位都表示對(duì)應(yīng)虛擬頁(yè)的相關(guān)屬性。

不管是那一級(jí)的頁(yè)表,它的功能就是建立虛擬地址和物理地址之間的映射關(guān)系,一個(gè)頁(yè)和一個(gè)頁(yè)框之間的映射關(guān)系體現(xiàn)在頁(yè)表項(xiàng)中。上圖中的物理頁(yè)基地址是 個(gè)抽象的說(shuō)明,如果當(dāng)前的頁(yè)表項(xiàng)位于頁(yè)全局目錄中,這個(gè)物理頁(yè)基址是指頁(yè)上級(jí)目錄所在物理頁(yè)的基地址;如果當(dāng)前頁(yè)表項(xiàng)位于頁(yè)表中,這個(gè)物理頁(yè)基地址是指最 終要訪問(wèn)數(shù)據(jù)所在物理頁(yè)的基地址。

(3)地址轉(zhuǎn)換過(guò)程

地址轉(zhuǎn)換過(guò)程有了上述的基本知識(shí),就很好理解四級(jí)頁(yè)表模式下如何將虛擬地址轉(zhuǎn)化為邏輯地址了。基本過(guò)程如下:

從CR3寄存器中讀取頁(yè)目錄所在物理頁(yè)面的基址(即所謂的頁(yè)目錄基址),從線性地址的第一部分獲取頁(yè)目錄項(xiàng)的索引,兩者相加得到頁(yè)目錄項(xiàng)的物理地址。

第一次讀取內(nèi)存得到pgd_t結(jié)構(gòu)的目錄項(xiàng),從中取出物理頁(yè)基址取出(具體位數(shù)與平臺(tái)相關(guān),如果是32系統(tǒng),則為20位),即頁(yè)上級(jí)頁(yè)目錄的物理基地址。

從線性地址的第二部分中取出頁(yè)上級(jí)目錄項(xiàng)的索引,與頁(yè)上級(jí)目錄基地址相加得到頁(yè)上級(jí)目錄項(xiàng)的物理地址。

第二次讀取內(nèi)存得到pud_t結(jié)構(gòu)的目錄項(xiàng),從中取出頁(yè)中間目錄的物理基地址。

從線性地址的第三部分中取出頁(yè)中間目錄項(xiàng)的索引,與頁(yè)中間目錄基址相加得到頁(yè)中間目錄項(xiàng)的物理地址。

第三次讀取內(nèi)存得到pmd_t結(jié)構(gòu)的目錄項(xiàng),從中取出頁(yè)表的物理基地址。

從線性地址的第四部分中取出頁(yè)表項(xiàng)的索引,與頁(yè)表基址相加得到頁(yè)表項(xiàng)的物理地址。

第四次讀取內(nèi)存得到pte_t結(jié)構(gòu)的目錄項(xiàng),從中取出物理頁(yè)的基地址。

從線性地址的第五部分中取出物理頁(yè)內(nèi)偏移量,與物理頁(yè)基址相加得到最終的物理地址。

第五次讀取內(nèi)存得到最終要訪問(wèn)的數(shù)據(jù)。

整個(gè)過(guò)程是比較機(jī)械的,每次轉(zhuǎn)換先獲取物理頁(yè)基地址,再?gòu)木€性地址中獲取索引,合成物理地址后再訪問(wèn)內(nèi)存。不管是頁(yè)表還是要訪問(wèn)的數(shù)據(jù)都是以頁(yè)為單 位存放在主存中的,因此每次訪問(wèn)內(nèi)存時(shí)都要先獲得基址,再通過(guò)索引(或偏移)在頁(yè)內(nèi)訪問(wèn)數(shù)據(jù),因此可以將線性地址看作是若干個(gè)索引的集合。

5.2 Linux中通過(guò)4級(jí)頁(yè)表訪問(wèn)物理內(nèi)存

linux中每個(gè)進(jìn)程有它自己的PGD( Page Global Directory),它是一個(gè)物理頁(yè),并包含一個(gè)pgd_t數(shù)組。

進(jìn)程的pgd_t數(shù)據(jù)見(jiàn) task_struct -> mm_struct -> pgd_t * pgd;

PTEs, PMDs和PGDs分別由pte_t, pmd_t 和pgd_t來(lái)描述。為了存儲(chǔ)保護(hù)位,pgprot_t被定義,它擁有相關(guān)的flags并經(jīng)常被存儲(chǔ)在page table entry低位(lower bits),其具體的存儲(chǔ)方式依賴于CPU架構(gòu)。

前面我們講了頁(yè)表處理的大多數(shù)函數(shù)信息,在上面我們又講了線性地址如何轉(zhuǎn)換為物理地址,其實(shí)就是不斷索引的過(guò)程。

通過(guò)如下幾個(gè)函數(shù),不斷向下索引,就可以從進(jìn)程的頁(yè)表中搜索特定地址對(duì)應(yīng)的頁(yè)面對(duì)象:

  • pgd_offset根據(jù)當(dāng)前虛擬地址和當(dāng)前進(jìn)程的mm_struct獲取pgd項(xiàng)
  • pud_offset參數(shù)為指向頁(yè)全局目錄項(xiàng)的指針 pgd 和線性地址 addr 。這個(gè)宏產(chǎn)生頁(yè)上級(jí)目錄中目錄項(xiàng) addr 對(duì)應(yīng)的線性地址。在兩級(jí)或三級(jí)分頁(yè)系統(tǒng)中,該宏產(chǎn)生 pgd ,即一個(gè)頁(yè)全局目錄項(xiàng)的地址
  • pmd_offset根據(jù)通過(guò)pgd_offset獲取的pgd 項(xiàng)和虛擬地址,獲取相關(guān)的pmd項(xiàng)(即pte表的起始地址)
  • pte_offset根據(jù)通過(guò)pmd_offset獲取的pmd項(xiàng)和虛擬地址,獲取相關(guān)的pte項(xiàng)(即物理頁(yè)的起始地址)

根據(jù)虛擬地址獲取物理頁(yè)的示例代碼詳見(jiàn)mm/memory.c中的函數(shù)follow_page

不同的版本可能有所不同,早起內(nèi)核中存在follow_page,而后來(lái)的內(nèi)核中被follow_page_mask替代,目前最新的發(fā)布4.4中為查找到此函數(shù)

我們從早期的linux-3.8的源代碼中, 截取的代碼如下

/**
 * follow_page - look up a page descriptor from a user-virtual address
 * @vma: vm_area_struct mapping @address
 * @address: virtual address to look up
 * @flags: flags modifying lookup behaviour
 *
 * @flags can have FOLL_ flags set, defined in <linux/mm.h>
 *
 * Returns the mapped (struct page *), %NULL if no mapping exists, or
 * an error pointer if there is a mapping to something not represented
 * by a page descriptor (see also vm_normal_page()).
 */
struct page *follow_page(struct vm_area_struct *vma, unsigned long address,
            unsigned int flags)
{
    pgd_t *pgd;
    pud_t *pud;
    pmd_t *pmd;
    pte_t *ptep, pte;
    spinlock_t *ptl;
    struct page *page;
    struct mm_struct *mm = vma->vm_mm;

    page = follow_huge_addr(mm, address, flags & FOLL_WRITE);
    if (!IS_ERR(page)) {
        BUG_ON(flags & FOLL_GET);
        goto out;
    }

    page = NULL;
    pgd = pgd_offset(mm, address);
    if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
        goto no_page_table;

    pud = pud_offset(pgd, address);
    if (pud_none(*pud))
        goto no_page_table;
    if (pud_huge(*pud) && vma->vm_flags & VM_HUGETLB) {
        BUG_ON(flags & FOLL_GET);
        page = follow_huge_pud(mm, address, pud, flags & FOLL_WRITE);
        goto out;
    }
    if (unlikely(pud_bad(*pud)))
        goto no_page_table;

    pmd = pmd_offset(pud, address);
    if (pmd_none(*pmd))
        goto no_page_table;
    if (pmd_huge(*pmd) && vma->vm_flags & VM_HUGETLB) {
        BUG_ON(flags & FOLL_GET);
        page = follow_huge_pmd(mm, address, pmd, flags & FOLL_WRITE);
        goto out;
    }
    if (pmd_trans_huge(*pmd)) {
        if (flags & FOLL_SPLIT) {
            split_huge_page_pmd(mm, pmd);
            goto split_fallthrough;
        }
        spin_lock(&mm->page_table_lock);
        if (likely(pmd_trans_huge(*pmd))) {
            if (unlikely(pmd_trans_splitting(*pmd))) {
                spin_unlock(&mm->page_table_lock);
                wait_split_huge_page(vma->anon_vma, pmd);
            } else {
                page = follow_trans_huge_pmd(mm, address,
                                 pmd, flags);
                spin_unlock(&mm->page_table_lock);
                goto out;
            }
        } else
            spin_unlock(&mm->page_table_lock);
        /* fall through */
    }
split_fallthrough:
    if (unlikely(pmd_bad(*pmd)))
        goto no_page_table;

    ptep = pte_offset_map_lock(mm, pmd, address, &ptl);

    pte = *ptep;
    if (!pte_present(pte))
        goto no_page;
    if ((flags & FOLL_WRITE) && !pte_write(pte))
        goto unlock;

    page = vm_normal_page(vma, address, pte);
    if (unlikely(!page)) {
        if ((flags & FOLL_DUMP) ||
            !is_zero_pfn(pte_pfn(pte)))
            goto bad_page;
        page = pte_page(pte);
    }

    if (flags & FOLL_GET)
        get_page(page);
    if (flags & FOLL_TOUCH) {
        if ((flags & FOLL_WRITE) &&
            !pte_dirty(pte) && !PageDirty(page))
            set_page_dirty(page);
        /*
         * pte_mkyoung() would be more correct here, but atomic care
         * is needed to avoid losing the dirty bit: it is easier to use
         * mark_page_accessed().
         */
        mark_page_accessed(page);
    }
    if ((flags & FOLL_MLOCK) && (vma->vm_flags & VM_LOCKED)) {
        /*
         * The preliminary mapping check is mainly to avoid the
         * pointless overhead of lock_page on the ZERO_PAGE
         * which might bounce very badly if there is contention.
         *
         * If the page is already locked, we don't need to
         * handle it now - vmscan will handle it later if and
         * when it attempts to reclaim the page.
         */
        if (page->mapping && trylock_page(page)) {
            lru_add_drain();  /* push cached pages to LRU */
            /*
             * Because we lock page here and migration is
             * blocked by the pte's page reference, we need
             * only check for file-cache page truncation.
             */
            if (page->mapping)
                mlock_vma_page(page);
            unlock_page(page);
        }
    }
unlock:
    pte_unmap_unlock(ptep, ptl);
out:
    return page;

bad_page:
    pte_unmap_unlock(ptep, ptl);
    return ERR_PTR(-EFAULT);

no_page:
    pte_unmap_unlock(ptep, ptl);
    if (!pte_none(pte))
        return page;

no_page_table:
    /*
     * When core dumping an enormous anonymous area that nobody
     * has touched so far, we don't want to allocate unnecessary pages or
     * page tables.  Return error instead of NULL to skip handle_mm_fault,
     * then get_dump_page() will return NULL to leave a hole in the dump.
     * But we can only make this optimization where a hole would surely
     * be zero-filled if handle_mm_fault() actually did handle it.
     */
    if ((flags & FOLL_DUMP) &&
        (!vma->vm_ops || !vma->vm_ops->fault))
        return ERR_PTR(-EFAULT);
    return page;
}

以上代碼可以精簡(jiǎn)為:

unsigned long v2p(int pid unsigned long va)
{
        unsigned long pa = 0;
        struct task_struct *pcb_tmp = NULL;
        pgd_t *pgd_tmp = NULL;
        pud_t *pud_tmp = NULL;
        pmd_t *pmd_tmp = NULL;
        pte_t *pte_tmp = NULL;

        printk(KERN_INFO"PAGE_OFFSET = 0x%lx\n",PAGE_OFFSET);
        printk(KERN_INFO"PGDIR_SHIFT = %d\n",PGDIR_SHIFT);
        printk(KERN_INFO"PUD_SHIFT = %d\n",PUD_SHIFT);
        printk(KERN_INFO"PMD_SHIFT = %d\n",PMD_SHIFT);
        printk(KERN_INFO"PAGE_SHIFT = %d\n",PAGE_SHIFT);

        printk(KERN_INFO"PTRS_PER_PGD = %d\n",PTRS_PER_PGD);
        printk(KERN_INFO"PTRS_PER_PUD = %d\n",PTRS_PER_PUD);
        printk(KERN_INFO"PTRS_PER_PMD = %d\n",PTRS_PER_PMD);
        printk(KERN_INFO"PTRS_PER_PTE = %d\n",PTRS_PER_PTE);

        printk(KERN_INFO"PAGE_MASK = 0x%lx\n",PAGE_MASK);

        //if(!(pcb_tmp = find_task_by_pid(pid)))
        if(!(pcb_tmp = findTaskByPid(pid)))
        {
                printk(KERN_INFO"Can't find the task %d .\n",pid);
                return 0;
        }
        printk(KERN_INFO"pgd = 0x%p\n",pcb_tmp->mm->pgd);

        /* 判斷給出的地址va是否合法(va<vm_end)*/
        if(!find_vma(pcb_tmp->mm,va))
        {
                printk(KERN_INFO"virt_addr 0x%lx not available.\n",va);
                return 0;
        }

        pgd_tmp = pgd_offset(pcb_tmp->mm,va);
        printk(KERN_INFO"pgd_tmp = 0x%p\n",pgd_tmp);
        printk(KERN_INFO"pgd_val(*pgd_tmp) = 0x%lx\n",pgd_val(*pgd_tmp));
        if(pgd_none(*pgd_tmp))
        {
                printk(KERN_INFO"Not mapped in pgd.\n");
                return 0;
        }

        pud_tmp = pud_offset(pgd_tmp,va);
        printk(KERN_INFO"pud_tmp = 0x%p\n",pud_tmp);
        printk(KERN_INFO"pud_val(*pud_tmp) = 0x%lx\n",pud_val(*pud_tmp));
        if(pud_none(*pud_tmp))
        {
                printk(KERN_INFO"Not mapped in pud.\n");
                return 0;
        }

        pmd_tmp = pmd_offset(pud_tmp,va);
        printk(KERN_INFO"pmd_tmp = 0x%p\n",pmd_tmp);
        printk(KERN_INFO"pmd_val(*pmd_tmp) = 0x%lx\n",pmd_val(*pmd_tmp));
        if(pmd_none(*pmd_tmp))
        {
                printk(KERN_INFO"Not mapped in pmd.\n");
                return 0;
        }

        /*在這里,把原來(lái)的pte_offset_map()改成了pte_offset_kernel*/
        pte_tmp = pte_offset_kernel(pmd_tmp,va);

        printk(KERN_INFO"pte_tmp = 0x%p\n",pte_tmp);
        printk(KERN_INFO"pte_val(*pte_tmp) = 0x%lx\n",pte_val(*pte_tmp));
        if(pte_none(*pte_tmp))
        {
                printk(KERN_INFO"Not mapped in pte.\n");
                return 0;
        }
        if(!pte_present(*pte_tmp)){
                printk(KERN_INFO"pte not in RAM.\n");
                return 0;
        }

        pa = (pte_val(*pte_tmp) & PAGE_MASK) | (va & ~PAGE_MASK);
        printk(KERN_INFO"virt_addr 0x%lx in RAM is 0x%lx t .\n",va,pa);
        printk(KERN_INFO"contect in 0x%lx is 0x%lx\n", pa, *(unsigned long *)((char *)pa + PAGE_OFFSET)
}

六、Linux 分頁(yè)機(jī)制的顯著優(yōu)勢(shì)

6.1內(nèi)存保護(hù)與隔離

在 Linux 的內(nèi)存管理體系中,分頁(yè)機(jī)制就像是一位忠誠(chéng)的衛(wèi)士,為系統(tǒng)的安全穩(wěn)定運(yùn)行保駕護(hù)航,而其中頁(yè)表項(xiàng)權(quán)限位則是這位衛(wèi)士手中的 “秘密武器”,在內(nèi)存保護(hù)和隔離方面發(fā)揮著關(guān)鍵作用。

每個(gè)進(jìn)程都擁有屬于自己的獨(dú)立頁(yè)表,這就如同每個(gè)進(jìn)程都有一個(gè)專屬的 “房間”,而頁(yè)表則是這個(gè)房間的 “門(mén)鎖” 和 “管家”。頁(yè)表項(xiàng)中的權(quán)限位,如讀(Read)權(quán)限位、寫(xiě)(Write)權(quán)限位和執(zhí)行(Execute)權(quán)限位等,就像是門(mén)鎖上的不同 “密碼”,嚴(yán)格控制著對(duì)內(nèi)存頁(yè)的訪問(wèn)權(quán)限 。例如,當(dāng)一個(gè)進(jìn)程試圖訪問(wèn)另一個(gè)進(jìn)程的內(nèi)存空間時(shí),由于其頁(yè)表中對(duì)應(yīng)的權(quán)限位不允許這樣的訪問(wèn),系統(tǒng)就會(huì)立即檢測(cè)到這種違規(guī)行為,并觸發(fā)一個(gè)頁(yè)錯(cuò)誤(Page Fault) 。這就好比有人拿著錯(cuò)誤的密碼試圖打開(kāi)別人房間的門(mén),門(mén)鎖會(huì)立刻發(fā)出警報(bào)。

操作系統(tǒng)在接收到頁(yè)錯(cuò)誤后,會(huì)迅速采取相應(yīng)措施,比如記錄錯(cuò)誤信息,終止違規(guī)進(jìn)程,以防止惡意進(jìn)程的攻擊和破壞,確保每個(gè)進(jìn)程的數(shù)據(jù)都能安全地存放在自己的 “房間” 里,實(shí)現(xiàn)了不同進(jìn)程之間內(nèi)存的有效隔離 。這種基于頁(yè)表項(xiàng)權(quán)限位的內(nèi)存保護(hù)機(jī)制,為 Linux 系統(tǒng)的安全性和穩(wěn)定性提供了堅(jiān)實(shí)的保障,讓各個(gè)進(jìn)程能夠在相互隔離的環(huán)境中穩(wěn)定運(yùn)行,避免了數(shù)據(jù)泄露和非法訪問(wèn)帶來(lái)的風(fēng)險(xiǎn)。

6.2高效內(nèi)存利用

分頁(yè)機(jī)制在提高內(nèi)存利用率方面,有著出色的表現(xiàn),堪稱內(nèi)存管理的 “效率大師”。它通過(guò)將內(nèi)存劃分為固定大小的頁(yè),巧妙地解決了內(nèi)存碎片這一棘手問(wèn)題。

在傳統(tǒng)的內(nèi)存分配方式中,由于進(jìn)程對(duì)內(nèi)存的需求大小不一,頻繁的分配和釋放內(nèi)存很容易產(chǎn)生內(nèi)存碎片。就像你有一塊大蛋糕,每次根據(jù)不同的需求切下大小不一的塊,時(shí)間久了,就會(huì)剩下很多小塊的 “邊角料”,這些 “邊角料” 因?yàn)樘《鵁o(wú)法被充分利用,造成了內(nèi)存的浪費(fèi)。而分頁(yè)機(jī)制則像是把蛋糕切成了大小相同的小塊,無(wú)論進(jìn)程需要多少內(nèi)存,都可以通過(guò)分配相應(yīng)數(shù)量的頁(yè)來(lái)滿足需求 。當(dāng)進(jìn)程結(jié)束后,釋放的頁(yè)又可以被系統(tǒng)重新分配,大大減少了內(nèi)存碎片的產(chǎn)生,提高了內(nèi)存的使用效率。

同時(shí),分頁(yè)機(jī)制還支持動(dòng)態(tài)內(nèi)存分配,能夠根據(jù)進(jìn)程的實(shí)際需求,靈活地分配和回收內(nèi)存頁(yè)。當(dāng)一個(gè)進(jìn)程需要更多的內(nèi)存時(shí),系統(tǒng)可以迅速為其分配新的頁(yè);當(dāng)進(jìn)程不再需要某些內(nèi)存時(shí),這些頁(yè)又能及時(shí)被回收,重新投入到其他需要的地方。這種動(dòng)態(tài)分配的方式,就像一個(gè)智能的資源分配器,能夠根據(jù)實(shí)際情況,合理地調(diào)配內(nèi)存資源,避免了內(nèi)存的閑置和浪費(fèi),使得系統(tǒng)能夠在有限的內(nèi)存條件下,高效地運(yùn)行多個(gè)進(jìn)程 。

例如,在一個(gè)同時(shí)運(yùn)行多個(gè)程序的 Linux 系統(tǒng)中,分頁(yè)機(jī)制可以根據(jù)每個(gè)程序的實(shí)時(shí)內(nèi)存需求,動(dòng)態(tài)地分配和回收內(nèi)存頁(yè),確保每個(gè)程序都能獲得足夠的內(nèi)存資源,同時(shí)又不會(huì)造成內(nèi)存的過(guò)度占用,極大地提高了內(nèi)存的利用率,提升了系統(tǒng)的整體性能。

七、分頁(yè)機(jī)制的應(yīng)用場(chǎng)景

7.1服務(wù)器場(chǎng)景

在服務(wù)器環(huán)境中,通常會(huì)同時(shí)運(yùn)行多個(gè)不同的進(jìn)程,這些進(jìn)程各自承擔(dān)著不同的任務(wù),比如 Web 服務(wù)器進(jìn)程負(fù)責(zé)處理網(wǎng)頁(yè)請(qǐng)求,數(shù)據(jù)庫(kù)服務(wù)器進(jìn)程負(fù)責(zé)數(shù)據(jù)的存儲(chǔ)和讀取,郵件服務(wù)器進(jìn)程負(fù)責(zé)郵件的收發(fā)等 。以一個(gè)典型的電商網(wǎng)站服務(wù)器為例,它可能同時(shí)運(yùn)行著 Web 服務(wù)進(jìn)程、數(shù)據(jù)庫(kù)服務(wù)進(jìn)程和緩存服務(wù)進(jìn)程等。Web 服務(wù)進(jìn)程需要處理大量用戶的頁(yè)面訪問(wèn)請(qǐng)求,數(shù)據(jù)庫(kù)服務(wù)進(jìn)程則要頻繁地進(jìn)行數(shù)據(jù)的查詢和更新操作,緩存服務(wù)進(jìn)程則負(fù)責(zé)存儲(chǔ)和管理常用的數(shù)據(jù),以提高訪問(wèn)速度 。

在這種多進(jìn)程并發(fā)運(yùn)行的情況下,內(nèi)存資源的管理變得至關(guān)重要。分頁(yè)機(jī)制就像是一位高效的資源調(diào)度員,為每個(gè)進(jìn)程分配獨(dú)立的虛擬內(nèi)存空間,確保它們?cè)诟髯缘?“小天地” 里運(yùn)行,互不干擾。當(dāng) Web 服務(wù)進(jìn)程收到用戶的頁(yè)面請(qǐng)求時(shí),它會(huì)在自己的虛擬內(nèi)存空間中查找和處理相關(guān)數(shù)據(jù),而不會(huì)影響到數(shù)據(jù)庫(kù)服務(wù)進(jìn)程和緩存服務(wù)進(jìn)程的正常運(yùn)行 。同時(shí),分頁(yè)機(jī)制通過(guò)頁(yè)表的映射,能夠快速地將虛擬地址轉(zhuǎn)換為物理地址,提高內(nèi)存訪問(wèn)的效率。當(dāng)數(shù)據(jù)庫(kù)服務(wù)進(jìn)程需要讀取或?qū)懭霐?shù)據(jù)時(shí),分頁(yè)機(jī)制可以迅速地定位到物理內(nèi)存中的數(shù)據(jù)位置,減少數(shù)據(jù)訪問(wèn)的延遲,保證數(shù)據(jù)庫(kù)操作的高效性 。

而且,分頁(yè)機(jī)制還能根據(jù)進(jìn)程的實(shí)際需求,動(dòng)態(tài)地分配和回收內(nèi)存頁(yè)。當(dāng)電商網(wǎng)站在促銷(xiāo)活動(dòng)期間,訪問(wèn)量大幅增加,Web 服務(wù)進(jìn)程需要更多的內(nèi)存來(lái)處理請(qǐng)求時(shí),分頁(yè)機(jī)制可以及時(shí)為其分配額外的內(nèi)存頁(yè);當(dāng)活動(dòng)結(jié)束后,訪問(wèn)量減少,分頁(yè)機(jī)制又能回收多余的內(nèi)存頁(yè),將其重新分配給其他有需要的進(jìn)程,從而實(shí)現(xiàn)內(nèi)存資源的高效利用,保障服務(wù)器在高負(fù)載情況下的穩(wěn)定運(yùn)行 。

7.2虛擬化場(chǎng)景

在虛擬化技術(shù)中,分頁(yè)機(jī)制扮演著不可或缺的角色,它是實(shí)現(xiàn)虛擬機(jī)內(nèi)存隔離與分配的關(guān)鍵所在。以 KVM(Kernel-based Virtual Machine)虛擬化技術(shù)為例,它基于 Linux 內(nèi)核,通過(guò)將 Linux 內(nèi)核轉(zhuǎn)變?yōu)橐粋€(gè) Hypervisor,能夠在同一臺(tái)物理主機(jī)上運(yùn)行多個(gè)虛擬機(jī) 。

每個(gè)虛擬機(jī)都仿佛是一臺(tái)獨(dú)立的物理計(jì)算機(jī),擁有自己獨(dú)立的操作系統(tǒng)和應(yīng)用程序,而分頁(yè)機(jī)制則為這些虛擬機(jī)提供了獨(dú)立的內(nèi)存空間,就像為每個(gè)虛擬機(jī)劃分了專屬的 “內(nèi)存區(qū)域”,確保它們之間的內(nèi)存相互隔離,不會(huì)出現(xiàn)數(shù)據(jù)泄露或相互干擾的情況。當(dāng)一個(gè)虛擬機(jī)中的應(yīng)用程序訪問(wèn)內(nèi)存時(shí),分頁(yè)機(jī)制會(huì)根據(jù)該虛擬機(jī)的頁(yè)表,將虛擬地址準(zhǔn)確地映射到對(duì)應(yīng)的物理內(nèi)存頁(yè)上,保證虛擬機(jī)的正常運(yùn)行 。

同時(shí),分頁(yè)機(jī)制還能實(shí)現(xiàn)內(nèi)存的高效分配。在物理主機(jī)內(nèi)存有限的情況下,分頁(yè)機(jī)制可以根據(jù)各個(gè)虛擬機(jī)的實(shí)際內(nèi)存需求,動(dòng)態(tài)地分配內(nèi)存頁(yè)。比如,當(dāng)一臺(tái)虛擬機(jī)運(yùn)行一個(gè)輕量級(jí)的應(yīng)用程序時(shí),它可能只需要較少的內(nèi)存,分頁(yè)機(jī)制就會(huì)為其分配適量的內(nèi)存頁(yè);而當(dāng)另一臺(tái)虛擬機(jī)運(yùn)行一個(gè)大型數(shù)據(jù)庫(kù)應(yīng)用程序時(shí),它需要大量的內(nèi)存,分頁(yè)機(jī)制會(huì)優(yōu)先滿足其需求,為其分配足夠的內(nèi)存頁(yè) 。

這種靈活的內(nèi)存分配方式,使得物理主機(jī)的內(nèi)存資源能夠得到充分利用,提高了虛擬化環(huán)境的整體性能 。此外,分頁(yè)機(jī)制還能支持內(nèi)存的共享。在一些情況下,多個(gè)虛擬機(jī)可能會(huì)運(yùn)行相同的操作系統(tǒng)或應(yīng)用程序,分頁(yè)機(jī)制可以讓這些虛擬機(jī)共享相同的物理內(nèi)存頁(yè),減少內(nèi)存的占用,進(jìn)一步提高內(nèi)存的利用率 。例如,在一個(gè)云計(jì)算數(shù)據(jù)中心中,通過(guò) KVM 虛擬化技術(shù)運(yùn)行著大量的虛擬機(jī),分頁(yè)機(jī)制的存在使得這些虛擬機(jī)能夠高效地共享物理主機(jī)的內(nèi)存資源,實(shí)現(xiàn)了資源的最大化利用,為用戶提供了穩(wěn)定、高效的云計(jì)算服務(wù) 。

責(zé)任編輯:武曉燕 來(lái)源: 深度Linux
相關(guān)推薦

2009-12-25 17:15:03

Linux內(nèi)存

2009-12-17 11:00:47

Linux內(nèi)存管理

2025-09-18 09:17:46

2015-10-09 10:22:47

分頁(yè)內(nèi)存尋址Linux

2021-10-17 23:53:17

內(nèi)存管理方式

2013-09-29 15:11:46

Linux運(yùn)維內(nèi)存管理

2025-03-26 00:21:00

2024-12-26 10:45:08

2009-10-20 16:35:26

Linux內(nèi)存管理

2025-03-26 00:00:05

2013-10-11 17:32:18

Linux運(yùn)維內(nèi)存管理

2025-01-02 11:06:22

2010-07-23 09:34:48

Python

2024-03-05 09:55:00

C++右值引用開(kāi)發(fā)

2020-09-30 06:50:35

Linux內(nèi)存尋址

2013-03-14 10:28:52

管理人才管理管理方式

2022-06-01 16:01:58

MySQL內(nèi)存管理系統(tǒng)

2010-09-26 13:23:13

JVM內(nèi)存管理機(jī)制

2023-10-18 13:31:00

Linux內(nèi)存

2023-10-16 23:57:35

Redis內(nèi)存
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

成人av中文字幕| 久草免费在线观看视频| 在线观看的黄色| 99re亚洲国产精品| 俺去啦;欧美日韩| 特级黄色片视频| 欧美极品少妇videossex| 欧美日一区二区三区在线观看国产免| 日韩一区二区在线观看| 国产精品美女黄网| 中日韩黄色大片| 成人免费电影网址| 亚洲最新视频在线观看| 国产不卡av在线免费观看| 调教驯服丰满美艳麻麻在线视频 | 久久久成人av毛片免费观看| 国产精品免费观看视频| av一区观看| 久久露脸国语精品国产91| 成人网18免费网站| 日韩精品一区二区三区视频在线观看 | 亚洲午夜激情视频| 欧美巨大xxxx| 欧美在线观看一区二区| 成人自拍网站| 天天爱天天做天天爽| 美女网站色精品尤物极品姐弟| 色av成人天堂桃色av| 国产一区二区在线网站| 免费一级a毛片| 国产精品jizz在线观看美国| 亚洲视频综合网| 中文字幕1区2区| 国产精品秘入口| 国产成人亚洲综合色影视| 色婷婷av一区二区三区在线观看 | 成人影院网站| 亚洲日本在线看| 欧美伦理一区二区| 亚洲第一色网站| 欧美黄色录像片| 亚洲精品美女网站| av免费看网址| 无遮挡动作视频在线观看免费入口 | 成人午夜精品一区二区三区| 国产欧美在线看| 97久久久久久久| 欧美69wwwcom| 久久久国产视频| 国产v亚洲v天堂无码久久久| а天堂8中文最新版在线官网| 丝袜国产日韩另类美女| 97涩涩爰在线观看亚洲| 欧美激情 亚洲| 国产亚洲亚洲国产一二区| 在线观看视频一区二区欧美日韩| 高清欧美精品xxxxx| 亚洲欧美成人影院| 国产成人h网站| 成人激情视频免费在线| 一级在线观看视频| 神马影视一区二区| 亚洲欧美精品一区| 国产网站无遮挡| 欧美大奶一区二区| 亚洲精品av在线播放| 在线播放av网址| 97超碰成人| 亚洲国产成人精品女人久久久 | 在线观看免费看片| 欧洲性视频在线播放| 中文成人av在线| 亚洲国产精品久久久久久女王| 韩日视频在线| 国产亚洲成av人在线观看导航| 国产成人精品午夜| 亚洲图片欧美日韩| 奇米精品一区二区三区四区 | 欧美日韩国产经典色站一区二区三区 | 成人激情视频| 中文字幕日韩精品在线观看| 手机毛片在线观看| 97视频热人人精品免费| 中文字幕亚洲专区| 999精品视频在线观看播放| 国产精一区二区| 日韩午夜精品视频| 真实乱偷全部视频| 欧美美女啪啪| 在线视频日韩精品| 在线观看美女av| 狠狠一区二区三区| 国产视频亚洲精品| 亚洲国产日韩一区无码精品久久久| 女生影院久久| 欧美日韩综合在线| 一级片黄色免费| 草草视频在线一区二区| 日韩精品欧美激情| 中文字幕第二区| 99精品视频在线| 九九热在线精品视频| 日本亚洲欧美在线| 玖玖玖国产精品| 国产精品自产拍在线观看| www.色呦呦| 国产亚洲午夜高清国产拍精品| 亚洲最新在线| 国产资源在线观看入口av| 色综合久久中文综合久久97 | 国产九色精品成人porny| 国产精品日韩欧美一区二区三区| 在线观看xxx| 国产精品久久久99| 日韩中字在线观看| 成人午夜一级| 日韩www在线| 成人一级片免费看| 亚洲天堂黄色| 国产女人精品视频| 三级网站在线看| 亚洲欧洲日韩女同| 久久久亚洲综合网站| www.精品久久| 久久久久久久精| 奇米777四色影视在线看| 午夜精品成人av| 日韩午夜在线播放| 国产破处视频在线观看| 一区二区激情| 久久久久久久久久国产精品| 波多野结衣视频免费观看| 成人精品电影在线观看| 免费电影一区| 超免费在线视频| 亚洲图片激情小说| 欧美综合在线观看视频| 国产精选久久| 中文字幕无线精品亚洲乱码一区| chinese国产精品| 91免费版在线| 国产成人亚洲精品无码h在线| 日本国产精品| 91av国产在线| 色视频在线观看| 欧美日韩午夜剧场| 丰满少妇在线观看资源站| 国产日本精品| 农村寡妇一区二区三区| 天天综合网站| 欧美日韩国产免费一区二区| 波多野结衣一二三四区| 麻豆精品视频在线| 中文字幕一区二区三区四区五区| 青青久久精品| 久久亚洲精品中文字幕冲田杏梨| 国产aⅴ爽av久久久久成人| 亚洲蜜臀av乱码久久精品| 久热精品在线观看视频| 亚洲国产精品久久久天堂| 亚洲a中文字幕| 免费不卡av| 日韩av在线直播| 无码人妻精品一区二| 中文字幕中文在线不卡住| 91香蕉国产线在线观看| 亚洲激情自拍| 91老司机在线| 少女频道在线观看高清 | 人成免费电影一二三区在线观看| 色综合咪咪久久| 国精品人伦一区二区三区蜜桃| 国产一区二区福利视频| 黄色一级在线视频| 欧美男男gaytwinkfreevideos| 国产精品欧美日韩一区二区| а√天堂8资源在线官网| 亚洲国产三级网| www.久久网| 夜夜亚洲天天久久| 成人影视免费观看| 韩日av一区二区| www.99热这里只有精品| 精品国产乱码久久久| 韩国福利视频一区| 极品美乳网红视频免费在线观看 | 中文字幕狠狠干| 日韩精品一二三区| 黄色一级片网址| 在线黄色网页| 亚洲乱码国产乱码精品精| 在线播放亚洲精品| 午夜视黄欧洲亚洲| 国产午夜精品福利视频| 成人做爰69片免费看网站| 九一精品在线观看| 亚洲午夜91| 亚洲国产高清国产精品| 免费福利视频一区| 91久久久久久久久久久久久| 久久男人天堂| 欧美大片日本大片免费观看| 亚洲影院在线播放| 亚洲精品乱码久久久久久| 四虎永久免费影院| 国产69精品一区二区亚洲孕妇| 天天爱天天操天天干| 国产一区美女| 一区二区三区观看| 亚洲桃色综合影院| 超碰97在线资源| 亚洲伦理一区二区| 国产99久久精品一区二区 夜夜躁日日躁 | 成人av电影在线| 精品久久久99| 老司机久久99久久精品播放免费| 800av在线免费观看| 9999精品视频| 26uuu亚洲伊人春色| 影院在线观看全集免费观看| 一本大道久久加勒比香蕉| 午夜性色福利视频| 激情综合婷婷| 国产精品久久久久久av福利软件| 天堂av资源在线| 337p亚洲精品色噜噜狠狠| 无码人妻精品一区二区三区9厂 | 国产一级爱c视频| 自拍视频亚洲| 99亚洲精品视频| 青青草91久久久久久久久| 免费在线成人av| 色天天色综合| 久久综合狠狠综合久久综青草| 国产区精品视频在线观看豆花| 亚洲japanese制服美女| 九色精品蝌蚪| 91久久国产自产拍夜夜嗨| 99热这里有精品| 91免费版网站入口| 99国内精品久久久久| 91精品在线影院| 国产激情综合| 99精彩视频在线观看免费| 日韩精品一区国产| 97人人模人人爽人人喊38tv| 欧美午夜在线播放| 岛国一区二区三区高清视频| 亚洲国产欧美在线观看| 不卡视频一区二区三区| 大伊香蕉精品在线品播放| 精品欧美一区二区在线观看视频| 欧美爱爱网站| 欧美专区一二三| 欧美精品一二| 青青草原国产免费| 欧美喷水视频| 玩弄中年熟妇正在播放| 免费精品视频| 亚欧美在线观看| 韩国女主播成人在线观看| 性生活在线视频| 成人av在线一区二区三区| 亚洲欧美色图视频| 国产片一区二区| 男女性高潮免费网站| 亚洲尤物在线视频观看| 国产精品xxxx喷水欧美| 色94色欧美sute亚洲线路二| 中文字幕在线播出| 亚洲一区二区欧美日韩| 日本视频免费在线| 色菇凉天天综合网| 国产精品毛片久久久久久久av| 日韩精品一区二区三区在线| 日韩亚洲视频在线观看| 少妇高潮久久久久久潘金莲| 亚洲欧美成人影院| 青青草成人在线| av在线精品| 精品不卡在线| 欧美韩日高清| 99精品在线免费视频| 精品一区二区三区免费视频| av电影在线播放| 国产精品午夜在线观看| 精品午夜福利视频| 在线亚洲免费视频| 亚洲国产精品无码久久| 亚洲天堂av电影| 青青草原国产在线| 国产成人午夜视频网址| 9999久久久久| 亚洲精品久久久久久一区二区| 在线不卡亚洲| 日本中文字幕观看| 久久亚洲二区三区| 亚洲精品乱码久久久久久不卡| 中文字幕精品三区| 日本中文字幕免费| 91精品国产色综合久久久蜜香臀| 日本护士...精品国| 欧美精品免费播放| 国内自拍亚洲| 玛丽玛丽电影原版免费观看1977| 91成人国产| 色综合色综合色综合色综合| 91丨porny丨在线| 久草视频免费在线播放| 欧美日韩一区二区在线视频| 午夜影院免费视频| 欧美丰满少妇xxxxx| 四虎国产精品成人免费影视| 欧美精彩一区二区三区| 伊人久久大香线蕉综合热线| 国产大片一区二区三区| 中文字幕av在线一区二区三区| 日日夜夜综合网| 精品国产sm最大网站| 中文字幕中文字幕在线十八区 | ccyy激情综合| 在线黄色的网站| 欧美成人免费大片| 123成人网| 欧美不卡三区| 一区二区三区福利| 第一页在线视频| 亚洲日穴在线视频| 国产一区二区麻豆| 日韩视频―中文字幕| 日本美女久久| 日产国产精品精品a∨| 性欧美长视频| 人人妻人人澡人人爽人人精品| 五月婷婷激情综合| 黑人乱码一区二区三区av| 九九综合九九综合| 中文字幕视频精品一区二区三区| 特级黄色录像片| 国产一区二区在线看| 日韩在线视频网址| 51精品秘密在线观看| 黄色免费在线看| 91丨九色丨国产在线| 亚洲色图国产| 国产成人精品一区二区在线小狼| 亚洲一区二区中文在线| 国产 欧美 自拍| 国内精品久久久久影院 日本资源| 中文字幕一区二区三区四区久久| 国产精品久久久久9999爆乳| av电影天堂一区二区在线观看| 日韩美女黄色片| 亚洲欧美日韩在线高清直播| 成人va天堂| 亚洲一卡二卡三卡| 黄页视频在线91| 国产在线视频二区| 亚洲国产视频网站| 姝姝窝人体www聚色窝| 91av视频在线| 欧美日韩xxxx| 亚洲欧美日本一区二区三区| 亚洲精品欧美激情| 天堂成人在线视频| 国产精品com| 中文字幕午夜精品一区二区三区| 欧美一级片在线免费观看| 亚洲成av人影院| 成人全视频高清免费观看| 国产中文字幕日韩| 91久久亚洲| 大胸美女被爆操| 日韩欧美国产一二三区| 亚洲综合电影| 一级特黄妇女高潮| 91在线小视频| 一级黄色大片网站| 久久久久久久97| 成人久久电影| 91精品又粗又猛又爽| 欧美午夜精品理论片a级按摩| 在线观看免费视频你懂的| 蜜桃91精品入口| 国产黄色91视频| 国产精品免费无遮挡无码永久视频| 久久综合久中文字幕青草| 久久成人福利| 91女神在线观看| 婷婷开心激情综合| 黄色网在线播放| 日韩欧美亚洲在线| 成人性生交大片免费看中文网站| 国产第一页在线观看| 欧美精品国产精品日韩精品| 大色综合视频网站在线播放| 精品久久久久久无码人妻| 欧美日韩视频在线观看一区二区三区| 蜜桃传媒在线观看免费进入| 亚洲一二区在线|