南縣做網(wǎng)站設(shè)計網(wǎng)站大全
多處理器系統(tǒng)兩種體系結(jié)構(gòu):
-
非一致內(nèi)存訪問(Non-Uniform Memory Access,NUMA):這種體系結(jié)構(gòu)下,內(nèi)存被劃分成多個內(nèi)存節(jié)點,每個節(jié)點由不同的處理器訪問。訪問一個內(nèi)存節(jié)點所需的時間取決于處理器和內(nèi)存節(jié)點之間的距離,因此處理器與內(nèi)存節(jié)點之間的距離會影響內(nèi)存訪問速度。
-
對稱多處理器(Symmetric Multi-Processor,SMP):這種體系結(jié)構(gòu)是一致內(nèi)存訪問(Uniform Memory Access,UMA)的一種形式,所有處理器對內(nèi)存的訪問時間是相同的,即無論處理器的位置如何,訪問內(nèi)存的開銷是相等的。
內(nèi)存模型
Linux內(nèi)核內(nèi)存模型是從處理器的角度看到的物理內(nèi)存分布,內(nèi)核管理不同內(nèi)存模型的方式存在差異。內(nèi)存管理子系統(tǒng)支持以下三種內(nèi)存模型:
-
平坦內(nèi)存(Flat Memory):在這種模型下,內(nèi)存的物理地址空間是連續(xù)的,且沒有空洞。這是最簡單的內(nèi)存模型,因為對于物理內(nèi)存的管理而言,只需按順序分配內(nèi)存即可。
-
不連續(xù)內(nèi)存(Discontiguous Memory):在這種模型下,內(nèi)存的物理地址空間存在空洞,但是這種模型可以高效地處理空洞。這是因為內(nèi)存管理子系統(tǒng)可以跟蹤哪些物理地址是已經(jīng)被占用,哪些是空閑的,然后在空閑內(nèi)存之間分配新的內(nèi)存。
-
稀疏內(nèi)存(Sparse Memory):在這種模型下,內(nèi)存的物理地址空間也存在空洞,但是如果要支持內(nèi)存熱插拔,只能選擇稀疏內(nèi)存模型。這是因為在內(nèi)存熱插拔時,可能會出現(xiàn)大量的空洞,如果采用不連續(xù)內(nèi)存模型,那么在進行內(nèi)存分配時,需要遍歷整個物理地址空間,這樣會造成不必要的開銷。而稀疏內(nèi)存模型可以維護一個可擴展的物理地址空間列表,只需在該列表中分配內(nèi)存即可。
三級結(jié)構(gòu)
內(nèi)存管理子系統(tǒng)使用節(jié)點、區(qū)域和頁三級結(jié)構(gòu)來描述物理內(nèi)存的管理。
-
節(jié)點:節(jié)點是指物理內(nèi)存的邏輯分組單元,通常對應(yīng)于具有特定特性或位置的一組物理內(nèi)存。每個節(jié)點包含一個或多個區(qū)域,用于管理一定范圍內(nèi)的物理內(nèi)存。
-
區(qū)域:區(qū)域是節(jié)點內(nèi)部的一個邏輯劃分,用于管理一定范圍內(nèi)的物理內(nèi)存頁。不同的區(qū)域可能具有不同的特性,例如可回收內(nèi)存、不可回收內(nèi)存等。常見的區(qū)域包括高速緩存區(qū)、低速緩存區(qū)、DMA區(qū)等。
-
頁:頁是內(nèi)存管理的最小單位,通常是以固定大小(如4KB)劃分的內(nèi)存塊。操作系統(tǒng)通過頁表來映射虛擬內(nèi)存和物理內(nèi)存之間的對應(yīng)關(guān)系,實現(xiàn)內(nèi)存的管理和地址轉(zhuǎn)換。
內(nèi)存節(jié)點(pglist_data)
內(nèi)存節(jié)點分為兩種情況:
-
對于NUMA(非一致性存儲訪問)體系的內(nèi)存節(jié)點,內(nèi)存節(jié)點根據(jù)處理器和內(nèi)存的距離劃分。在NUMA架構(gòu)中,不同的處理器可能與不同的內(nèi)存區(qū)域相連,因此系統(tǒng)將內(nèi)存劃分為不同的節(jié)點,以便更有效地管理和分配內(nèi)存資源。
-
在具有不連續(xù)內(nèi)存的NUMA系統(tǒng)中,內(nèi)存節(jié)點表示比區(qū)域的級別更高的內(nèi)存區(qū)域,根據(jù)物理地址是否連續(xù)劃分。在這種情況下,每塊物理地址連續(xù)的內(nèi)存被視為一個內(nèi)存節(jié)點。這種劃分方式可以幫助內(nèi)核更好地管理非連續(xù)內(nèi)存的分配和使用,確保系統(tǒng)能夠有效地利用所有可用的物理內(nèi)存空間。
typedef struct pglist_data {struct zone node_zones[MAX_NR_ZONES]; // 內(nèi)存區(qū)域數(shù)組struct zonelist node_zonelists[MAX_ZONELISTS]; // 備用區(qū)域列表int nr_zones; // 該節(jié)點包含的內(nèi)存區(qū)域數(shù)量#ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */struct page *node_mem_map; // 內(nèi)存映射表,存儲每個物理頁的信息#ifdef CONFIG_PAGE_EXTENSIONstruct page_ext *node_page_ext; // 頁的擴展屬性
#endif#endifunsigned long node_start_pfn; // 該節(jié)點的起始物理頁號unsigned long node_present_pages; // 物理頁總數(shù),即該節(jié)點上存在的物理頁數(shù)量unsigned long node_spanned_pages; // 物理頁范圍的總長度,包括空洞,即該節(jié)點的物理頁范圍總大小int node_id; // 節(jié)點標識符...
}
pglist_data
結(jié)構(gòu)體定義了一個內(nèi)存節(jié)點的數(shù)據(jù)結(jié)構(gòu)。
node_zones
:內(nèi)存區(qū)域數(shù)組,用于存儲該節(jié)點內(nèi)每個內(nèi)存區(qū)域的信息。node_zonelists
:備用區(qū)域列表,用于存儲備用的內(nèi)存區(qū)域的信息。nr_zones
:記錄該節(jié)點包含的內(nèi)存區(qū)域的數(shù)量。node_mem_map
:內(nèi)存映射表,用于存儲每個物理頁的信息。僅在沒有使用 SPARSEMEM 的情況下有效。node_page_ext
:頁的擴展屬性,用于存儲與頁相關(guān)的額外屬性。僅在啟用了 CONFIG_PAGE_EXTENSION 的情況下有效。node_start_pfn
:該節(jié)點的起始物理頁號。node_present_pages
:該節(jié)點上存在的物理頁數(shù)量。node_spanned_pages
:該節(jié)點的物理頁范圍總大小,包括空洞。node_id
:節(jié)點標識符。
內(nèi)存區(qū)域(zone)
內(nèi)存區(qū)域是將物理內(nèi)存按照不同屬性進行劃分的一種方式。每個內(nèi)存區(qū)域都有一個唯一的類型標識,類型包括 ZONE_DMA、ZONE_DMA32、ZONE_NORMAL、ZONE_HIGHMEM、ZONE_MOVABLE、ZONE_DEVICE 等。
enum zone_type {// DMA區(qū)域,直接內(nèi)存訪問。如果有些設(shè)備不能直接訪問所有內(nèi)存,需要使用DMA區(qū)域
#ifdef CONFIG_ZONE_DMAZONE_DMA,
#endif// DMA32區(qū)域64位系統(tǒng),如果既要支持只能直接訪問16MB以下的內(nèi)存設(shè)備,又要支持只能直接訪問4GB以下內(nèi)存的32設(shè)備,必須使用此DMA32區(qū)域
#ifdef CONFIG_ZONE_DMA32ZONE_DMA32,
#endif/** Normal addressable memory is in ZONE_NORMAL. DMA operations can be* performed on pages in ZONE_NORMAL if the DMA devices support* transfers to all addressable memory.*/// 普通區(qū)域:直接映射到內(nèi)核虛擬地址空間的內(nèi)存區(qū)域,又稱為普通區(qū)域ZONE_NORMAL,#ifdef CONFIG_HIGHMEM/** A memory area that is only addressable by the kernel through* mapping portions into its own address space. This is for example* used by i386 to allow the kernel to address the memory beyond* 900MB. The kernel will set up special mappings (page* table entries on i386) for each page that the kernel needs to* access.*/// 高端內(nèi)存區(qū)域:內(nèi)核和用戶地址空間按1:3劃分,內(nèi)核地址空間只有1GB,不能把1GB以上的內(nèi)存直接映射到內(nèi)核地址ZONE_HIGHMEM,
#endif// 可移動區(qū)域:它是一個偽內(nèi)存區(qū)域,用來存放內(nèi)存碎片ZONE_MOVABLE,#ifdef CONFIG_ZONE_DEVICE// 設(shè)備區(qū)域:支持持久內(nèi)存熱插拔增加的內(nèi)存區(qū)域,每個內(nèi)存區(qū)域有一個zone結(jié)構(gòu)體來描述ZONE_DEVICE,
#endif__MAX_NR_ZONES
};
這樣整理后的代碼更加清晰易懂,注釋也更容易理解各個內(nèi)存區(qū)域的作用。
ZONE_DMA
:適用于 DMA 的內(nèi)存區(qū)域,長度受處理器類型的限制。在 IA-32 計算機上,限制為 16 MiB。ZONE_DMA32
:適用于可使用 32 位地址字尋址的 DMA 的內(nèi)存區(qū)域。在 64 位系統(tǒng)上,ZONE_DMA 和 ZONE_DMA32 有所不同。在 32 位計算機上,ZONE_DMA32 區(qū)域為空,即長度為 0 MiB。ZONE_NORMAL
:直接映射區(qū)域,標記了可直接映射到內(nèi)核段的普通內(nèi)存區(qū)域。這是在所有體系結(jié)構(gòu)上保證都會存在的唯一內(nèi)存區(qū)域。但是,該地址范圍并不一定對應(yīng)實際的物理內(nèi)存,例如在某些系統(tǒng)中,所有內(nèi)存都屬于 ZONE_DMA32 范圍,而 ZONE_NORMAL 區(qū)域為空。ZONE_HIGHMEM
:該內(nèi)存區(qū)域是早期 32 位體系結(jié)構(gòu)的產(chǎn)物,因為內(nèi)核和用戶地址空間是 1:3 劃分的,所以不能將內(nèi)核 1GB 以上的內(nèi)存直接映射到內(nèi)核地址空間。在 64 位系統(tǒng)上,由于地址空間非常大,不存在這種問題。ZONE_MOVABLE
:可移動區(qū)域,是一個偽內(nèi)存區(qū)域,用于防止內(nèi)存碎片。可以用于分配無法被移動的內(nèi)存對象的區(qū)域,將該區(qū)域中的頁框移動到另一個區(qū)域,并釋放原始區(qū)域。ZONE_DEVICE
:持久內(nèi)存熱插拔增加的區(qū)域,用于支持設(shè)備驅(qū)動程序動態(tài)分配內(nèi)存。
一個內(nèi)存節(jié)點可能包含多個內(nèi)存區(qū)域,這些區(qū)域的類型和數(shù)量可以根據(jù)系統(tǒng)的需求進行配置。每個內(nèi)存區(qū)域都有一組特定的操作函數(shù)集合,用于管理該區(qū)域中的頁框。通過內(nèi)存區(qū)域的劃分,可以更加有效地管理和利用物理內(nèi)存。
冷熱頁
-
struct zone
的pageset
成員用于實現(xiàn)冷熱分配器:在Linux內(nèi)核中,為了提高內(nèi)存管理的效率和性能,使用了冷熱頁(Cold and Hot Pages)的概念。具體來說,在每個內(nèi)存區(qū)域(zone)中,定義了一個pageset
結(jié)構(gòu)體,用于管理該區(qū)域中的冷熱頁。 -
熱頁指的是已經(jīng)加載到CPU的高速緩存中的頁面,與內(nèi)存中的其他頁相比,其數(shù)據(jù)結(jié)構(gòu)可以更快地被訪問。冷頁則指不在CPU高速緩存中的頁面,當需要訪問它們時,需要從內(nèi)存中讀取數(shù)據(jù),這會導(dǎo)致較高的延遲。
-
在多處理器系統(tǒng)中,每個CPU都有一個或多個高速緩存。由于各個CPU具有獨立的高速緩存,因此對冷熱頁的管理必須是針對每個CPU獨立進行的。每個CPU都有自己的冷熱分配器(
pageset
),用于管理該CPU的熱頁和冷頁。這樣可以避免多個CPU之間相互競爭同一份冷熱頁管理的問題,提高了系統(tǒng)的并發(fā)性和性能。
// 每CPU頁面結(jié)構(gòu)體定義
struct per_cpu_pages {int count; // 列表中頁面數(shù)量int high; // 高水位標記,需要清空int batch; // 伙伴系統(tǒng)添加/移除的塊大小// 頁面列表,每個遷移類型在PCP列表上存儲一個struct list_head lists[MIGRATE_PCPTYPES];
};
-
count
記錄了與該列表相關(guān)的頁面數(shù)量。它表示當前列表中的頁的數(shù)量。 -
high
是一個水印(watermark)。當count
的值超過了high
時,表示列表中的頁太多了,需要進行一些處理。這個水印可以用來判斷列表是否過載。 -
batch
表示每次添加頁的參考值。在填充CPU高速緩存時,通常不是一次只填充一個頁面,而是以塊為單位填充,batch
就是指定每次填充的頁數(shù)。 -
lists
是一個數(shù)組,用于存儲不同遷移類型的頁面列表。每個遷移類型對應(yīng)一個列表,在PCP(per-CPU Page)列表上存儲。
物理頁(page)
頁是內(nèi)存管理的最小單位:在內(nèi)存管理中,頁是內(nèi)存的基本單位,頁面中的內(nèi)存物理地址是連續(xù)的。在Linux內(nèi)核中,物理頁被視為內(nèi)存管理的基本單位,即內(nèi)核中的內(nèi)存管理單元MMU將物理頁作為基本單位進行管理。
不同體系結(jié)構(gòu)支持不同的頁大小:不同的計算機體系結(jié)構(gòu)支持不同大小的頁。例如,32位體系結(jié)構(gòu)通常支持4KB的頁,而64位體系結(jié)構(gòu)通常支持8KB的頁。另外,像MIPS64架構(gòu)體系可能支持更大的頁,比如16KB的頁。
每個物理頁對應(yīng)一個page結(jié)構(gòu)體:在Linux內(nèi)核中,每個物理頁都對應(yīng)一個稱為頁描述符(page structure)的數(shù)據(jù)結(jié)構(gòu),用于描述和管理該物理頁的相關(guān)信息。每個內(nèi)存節(jié)點的pglist_data
實例中的成員node_mem_map
指向該內(nèi)存節(jié)點包含的所有物理頁的頁描述符組成的數(shù)組。
struct page {unsigned long flags; // 原子標志,有些情況下會異步更新union {struct { // 頁面緩存和匿名頁面struct list_head lru;// 如果最低位為0,則指向 inode 的 address_space 或為 NULL// 如果頁映射為匿名地址,最低位置位,而且指針指向 anon_vma 對象struct address_space *mapping;pgoff_t index; // 映射中的偏移量// 用于映射私有、不透明數(shù)據(jù)// 如果設(shè)置了 PagePrivate,則通常用于 buffer_heads// 如果設(shè)置了 PageSwapCache,則用于 swp_entry_t// 如果設(shè)置了 PageBuddy,則用于伙伴系統(tǒng)中的階unsigned long private;};struct { // slab、slob 和 slubunion {struct list_head slab_list;struct { // 部分頁面struct page *next;
#ifdef CONFIG_64BITint pages; // 剩余頁面數(shù)int pobjects; // 近似計數(shù)
#elseshort int pages;short int pobjects;
#endif};};struct kmem_cache *slab_cache; // 非 slob 時的 kmem_cache 指針/* 雙字邊界 */void *freelist; // 第一個空閑對象union {void *s_mem; // slab 分配器的第一個對象unsigned long counters; // SLUB 計數(shù)器struct { // SLUBunsigned inuse:16;unsigned objects:15;unsigned frozen:1;};};};// 其他字段...};
};
-
flags
:表示頁的各種狀態(tài)和屬性的標志位。這些標志位在某些情況下會被異步更新。 -
union
:使用聯(lián)合體來存儲不同類型的頁的信息。-
對于頁面緩存和匿名頁面(Page cache and anonymous pages):
lru
:用于將頁面鏈接到 LRU(Least Recently Used)鏈表,以進行頁面置換。mapping
:指向 inode 的 address_space 或為 NULL。如果頁面映射為匿名地址,則最低位置位且指針指向anon_vma
對象。index
:表示頁面在映射中的偏移量。private
:用于映射私有、不透明數(shù)據(jù)。根據(jù)不同的標志位,可以用于不同的目的,如PagePrivate
用于buffer_heads
,PageSwapCache
用于swp_entry_t
,PageBuddy
用于伙伴系統(tǒng)中的階。
-
對于 slab、slob 和 slub:
slab_list
或next
:用于管理頁面的鏈表結(jié)構(gòu),對于 slab 和 slob 分配器,用于鏈接已分配和未分配的頁面;對于 slub 分配器,用于鏈接部分頁面。slab_cache
:對于 slab 分配器,指向相關(guān)的 kmem_cache 結(jié)構(gòu)體;對于 slob 分配器和 slub 分配器,該字段不使用。freelist
或s_mem
:對于 slab 分配器,指向第一個空閑對象;對于 slob 分配器和 slub 分配器,用于存儲其他信息,如計數(shù)器、使用中的對象數(shù)量等。
-
頁表
頁表是操作系統(tǒng)中用于實現(xiàn)虛擬內(nèi)存到物理內(nèi)存映射的重要數(shù)據(jù)結(jié)構(gòu)。層次化的頁表結(jié)構(gòu)被設(shè)計用來支持對大地址空間的快速、高效管理。
-
內(nèi)存地址的分解:
根據(jù)四級頁表結(jié)構(gòu),虛擬內(nèi)存地址被分解為5部分,其中4個表項用于選擇頁,1個索引表示頁內(nèi)位置。每個指針末端的幾個比特位用于指定所選頁幀內(nèi)部的位置,具體的比特位數(shù)由PAGE_SHIFT指定。PMD_SHIFT指定了頁內(nèi)偏移量和最后一級頁表項所需比特位的總數(shù)。通過減去PAGE_SHIFT,可以得到最后一級頁表項索引所需的比特位數(shù)。類似地,PUD_SHIFT由PMD_SHIFT加上中間層頁表索引所需的比特位長度,而PGDIR_SHIFT由PUD_SHIFT加上上層頁表索引所需的比特位長度。計算全局頁目錄中一項所能尋址的部分地址空間長度,可以通過以2為底的對數(shù)計算得到PGDIR_SHIFT。 -
頁表的格式:
內(nèi)核提供了4個數(shù)據(jù)結(jié)構(gòu)來表示頁表項的結(jié)構(gòu):
- pgd_t用于全局頁目錄項
- pud_t用于上層頁目錄項
- pmd_t用于中間頁目錄項
- pte_t用于直接頁表項
- 特定于PTE的信息:
最后一級頁表中的項不僅包含了指向頁的內(nèi)存位置的指針,還包含了與頁面相關(guān)的附加信息。每種體系結(jié)構(gòu)都需要提供兩個東西,以便內(nèi)存管理子系統(tǒng)能夠修改pte_t項中額外的比特位。這兩個東西分別是保存額外比特位的__pgprot數(shù)據(jù)類型,以及用于修改這些比特位的pte_modify函數(shù)。
通過以上分析,我們可以更好地理解頁表的設(shè)計原理和結(jié)構(gòu),以及各個級別的頁表項在管理地址空間時的作用和關(guān)聯(lián)。如果您有任何進一步的問題或需要更多解釋,請隨時提出。
查詢和設(shè)置內(nèi)存頁與體系結(jié)構(gòu)相關(guān)狀態(tài)的函數(shù)
-
查詢函數(shù):
pte_present()
:檢查給定頁表項是否存在于內(nèi)存中。pte_write()
:檢查給定頁表項是否可寫。pte_user()
:檢查給定頁表項是否為用戶空間可訪問。pte_dirty()
:檢查給定頁表項是否被修改過。pte_young()
:檢查給定頁表項是否被訪問過。
-
設(shè)置函數(shù):
set_pte()
:設(shè)置給定頁表項的內(nèi)容。set_pte_at()
:在指定地址處設(shè)置頁表項的內(nèi)容。pte_clear()
:清除給定頁表項的內(nèi)容。pte_mkwrite()
:將只讀頁表項轉(zhuǎn)換為可寫。pte_mkdirty()
:標記頁表項已被修改。
-
體系結(jié)構(gòu)相關(guān)函數(shù):
pgd_index()
:獲取全局頁目錄項的索引。pmd_offset()
:獲取中間頁目錄項的指針。pud_offset()
:獲取上層頁目錄項的指針。pte_offset_kernel()
:獲取內(nèi)核頁表項的指針。pfn_to_page()
:將物理頁框號轉(zhuǎn)換為對應(yīng)的頁結(jié)構(gòu)體。
創(chuàng)建和操作頁表項的函數(shù)
-
創(chuàng)建頁表項:
pte_alloc()
:分配一個新的頁表項。pte_alloc_one()
:分配一個新的單個頁表項。pte_alloc_kernel()
:分配一個新的內(nèi)核頁表項。
-
釋放頁表項:
pte_free()
:釋放一個頁表項的內(nèi)存。pte_free_kernel()
:釋放一個內(nèi)核頁表項的內(nèi)存。
-
操作頁表項:
pte_clear()
:清除給定頁表項的內(nèi)容。pte_val()
:獲取頁表項的原始值。set_pte()
:設(shè)置指定頁表項的內(nèi)容。pte_mkclean()
:將頁表項標記為干凈(未修改)。pte_mkdirty()
:將頁表項標記為臟(已修改)。pte_present()
:檢查給定頁表項是否存在于內(nèi)存中。pte_write()
:檢查給定頁表項是否可寫。
參考:Linux內(nèi)核源碼分析(內(nèi)存調(diào)優(yōu)/文件系統(tǒng)/進程管理/設(shè)備驅(qū)動/網(wǎng)絡(luò)協(xié)議棧)教程
Linux內(nèi)核源碼系統(tǒng)性學習
>>>