ARM 學習筆記(二)

參考文獻:《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition》

1、MMU

1.1 背景

??早期的內存是比較小的,一般是幾十k,不過相應的程序也是比較小的,這時程序可以直接加載到內存中運行。后來為了支持多個程序的并行,內存中出現了固定分區,在編譯階段將不同程序,劃分在不同的內存區域上。這種方式存在不少問題:一是內存分區大小與程序大小要匹配,二是地址空間無法動態的增長。于是內存動態分區思想便誕生了,內存上線劃出一塊區域給操作系統,然后剩余的內存空間給用戶進程使用,這樣用戶程序所使用的內存空間,跟隨程序大小及數目進行變動。

??不論是靜態分析,還是動態分區,都存在一些問題:進程地址空間安全問題、內存使用效率低。為了能夠讓多程序安全、高效地并行運行,物理內存中需要存放多個程序的代碼及數據,這時虛擬內存便誕生了。不得不說虛擬內存是一個偉大的發明,一方面它讓每個程序認為自己是獨自、連續的使用內存,另一方面,每個程序之間的內存形成了安全隔離,避免程序破壞彼此的內存。

??后來隨著軟件的快速發展,一個程序的大小變得很大,這時物理內存大小跟不上程序大小增加的速度。這樣便不能將整個程序加載到物理內存中,一是物理內存沒有這么大,二是如果將整個程序加載到內存,為了多程序并行,就需要將大量的數據及代碼換入、換出,這導致程序運行效率低下。虛擬內存并沒解決高效使用內存的問題,好在程序運行遵循時間、空間局部性原理,進而出現了分頁機制。分頁機制從根本上解決了高效使用物理內存的問題。每次只需要將幾頁的代碼、數據從磁盤中加載到內存,程序就能正常運行。當程序運行的過程中,需要新的代碼、數據會產生缺頁異常,這些代碼、數據就會從磁盤加載到內存,然后程序從異常恢復正常運行。

??對于支持虛擬內存,分頁機制的系統,處理器直接尋址虛擬地址,這個地址不會直接發給內存控制器,而是先發給內存管理單元(Memory Manager Unit,MMU)。MMU 就是負責將虛擬地址轉換和翻譯成物理地址的一個硬件模塊,其實 MMU 所做的事,完全可以通過 CPU 來實現。為啥還要一個 MMU 硬件模塊呢?就是為了提升虛擬地址到物理地址轉換的速度,減少轉換所消耗的時間。MMU 包含兩個模塊TLB(Translation Lookaside Buffer)和TWU(Table Walk Unit)。TLB 是一個高速緩存,用于緩存頁表轉換的結果,從而縮短頁表查詢的時間。TWU 是一個頁表遍歷模塊,頁表是由操作系統維護在物理內存中,但是頁表的遍歷查詢是由 TWU 完成的,這樣減少對 CPU 資源的消耗。

在這里插入圖片描述

??虛擬內存及分頁機制的出現,解決了進程地址空間安全性的問題和內存使用效率低的問題,但是也引入了系統性能變差的問題。本來 CPU 可以直接通過訪存執行程序,但是現在引入了虛擬地址到物理地址的轉換。MMU 硬件模塊的出現,就是為了解決這個性能問題。因此,幾 G 運行內存的電腦,可以并行運行幾十G的多程序,讓你在聽歌的同時,能夠并行處理編輯文檔,下載電影,收發郵件等。

1.2 主要功能

  • 地址翻譯
    • 在用戶訪問內存時,將用戶訪問的虛擬地址翻譯為實際的物理地址,以便 CPU 對實際的物理地址進行訪問。
  • 訪問權限控制
    • 可以對一些虛擬地址進行訪問權限控制,以便于對用戶程序的訪問權限和范圍進行管理,如代碼段一般設置為只讀,如果有用戶程序對代碼段進行寫操作,系統會觸發異常。
  • 引申的物理內存管理
    • 對系統的物理內存資源進行管理,為用戶程序提供物理內存的申請、釋放等操作接口。

2、Translation tables

2.1 Linux 中的頁表

??Linux 內核通過多級頁表管理虛擬地址到物理地址的轉換。不同處理器架構可根據需求選擇頁表級數,主要組件包括:

縮寫全稱作用描述典型x86_64位偏移
PGDPage Global Directory頂級頁表結構47-39位
P4DPage 4th-level Directory第四級目錄(4.11新增)未固定
PUDPage Upper Directory上層目錄38-30位
PMDPage Middle Directory中間目錄29-21位
PTEPage Table Entry最終頁表項指向物理頁幀 20-12位

Linux 4.11 之前:

  • 最大支持4級頁表(PGD→PUD→PMD→PTE)

實際使用級數由 CPU 架構決定

  • 4級:PGD+PUD+PMD+PTE(如x86_64常規4KB頁)
  • 3級:PGD+PMD+PTE
  • 2級:PGD+PTE (常見的 ARMv7)

Linux 4.11 及之后:

  • 引入P4D 級(位于 PGD 和 PUD 之間),擴展至 5 級頁表
  • 新增級數應對48位以上地址空間(如 5 級分頁應對 57 位地址)

注意:
PGD、PUD、PMD、PTE 這些,都是 Linux 中的稱呼。在不同 CPU 架構手冊中,叫法可能各有不同。例如,在 ARMv7 中,對于只支持二級頁表的情況下,PGD 稱之為 First-level table,PTE 稱之為 Second-level table。

??在下面的文章中,為了符合 ARM 手冊,將 translation table,翻譯成轉換表,可以理解成,就是頁表的含義。

2.2 ARMv7 中的頁表

ARMv7-A 定義了兩種轉換表格式 :

Short-descriptor format

這是一種基礎格式,是未包含 大物理地址擴展(LPAE) 的實現中唯一支持的格式。它在轉換表中使用了 32 位的描述符條目,并提供了:

  • 提供兩級地址轉換
  • 32-bit 輸入地址
  • 輸出地址可高達 40-bit
  • 通過使用 supersections 實現 32 位以上地址的支持,最小內存單位為 16MB
  • 支持無訪問域、客戶端域和管理器域
  • 32-bit 頁表項

Long-descriptor format

這是一種 可選格式。大型物理地址擴展增加了對這種格式的支持。它在轉化表中使用了64位的描述符條目,并提供了:

  • 最多三級地址查找
  • 第二級轉換時,輸入地址可以高達40位
  • 輸出地址高達40位
  • 4KB assignment granularity across the entire PA range.
  • 不支持域,所有存儲區都被視為在客戶域中
  • 64-bit 位頁表項
  • 固定 4kB 的表大小,除非輸入地址被截斷

??本篇文章,皆以常見的 Short-descriptor format 為例進行講解。關于 Long-descriptor format ,有興趣的可以自行閱讀 ARM 手冊相關內容。

轉換表格式,其實就是頁表格式。所謂的 descriptor,描述的就是各級 頁表 的 頁表項

3. Short-descriptor format

3.1 Short-descriptor translation table format descriptors

??短描述符轉換表格式支持以內存段(memory sections)或內存頁(memory pages)為基礎的內存映射

  • Spersections: 16MB 的內存塊(memory block)
  • Sections: 1MB 的內存塊
  • Large pages: 64KB 的內存塊
  • Small pages: 4KB 的內存塊

這其中,除了 Small pages,其余僅使用單個 TLB 表項映射大區域內存。

Short-descriptor format 中是否支持 Spersections 是 implementation DEFINED

當使用 Short-descriptor translation table format 時,內存中保留兩級轉換表:

First-level table
一級表存儲一級描述符,每個描述符包含:

  • 段(Section)和超級段(Supersection)的基地址與轉換屬性
  • 大頁(Large page)或小頁(Small page)的轉換屬性及指向二級表的指針

Second-level tables
二級表存儲二級描述符,包含:

  • 大頁(Large page)或小頁(Small page)基地址與轉換屬性

在短描述符格式(Short-descriptor format)中,二級表可稱為頁表(Page tables)

注:每個二級表需占用 1KB 內存空間。一條頁表項 4 字節,共有 2^8 個頁表項
First-level table 在 Linux 中又叫做一級頁表,PGD
Second-level tables 在 Linux 中又叫做二級頁表,PTE

轉換表中的描述符通常分為以下類型:

  • 無效條目/故障條目(Invalid or fault entry)
  • 頁表條目(Page table entry):指向下一級翻譯表
  • 頁條目/段條目(Page or section entry):定義內存訪問屬性
  • 保留格式(Reserved format)
    在這里插入圖片描述

從上圖也可以看到,Large pages、 Small pages 才支持二級頁表。Spersections 和 Sections 只支持一級頁表

3.1.1 一級轉換表

在這里插入圖片描述
最后兩位 bits[1:0] 決定描述符類型

  • 0b00, Invalid
  • 0b01, Page table
  • 0b10, Section or Supersection
  • 0b11, Section or Supersection, if the implementation supports the PXN attribute
  • 0b11, Reserved, UNK/SBZP, if the implementation does not support the PXN attribute

舉個例子,當操作系統全部使用 Section 作為一級頁表中的最小尋址單位時,共 12 位地址表示。因為 Section 不涉及到二級頁表,所以頁表項最多為 212。而頁內偏移為 [19:0] ,所以頁大小為 220。綜上,可以表示的最大虛擬空間大小為:212 * 220

3.1.2 二級轉換表

在這里插入圖片描述
最后兩位 bits[1:0] 決定描述符類型

  • 0b00, Invalid
  • 0b01, Large page
  • 0b1x, Small page

舉個例子,使用 Small Page 作為二級頁表的最小尋址單位時,共 20 位地址表示,所能表示的最大虛擬地址范圍就是 220* 212 = 4GB。頁內偏移為 [11:0] ,所以頁大小為 212

3.2 Short-descriptor translation table format descriptors 中的內存屬性

??把內存屬性單獨拿出一個章節來講,是因為內存屬性很重要.。

  • TEX[2:0], C, B
    • 內存區域屬性位
    • 詳見第 5 章節
  • XN bit
    • 全局不可執行。如果執行,會觸發 Permission fault
  • PXN bit, when supported
    • Privileged Execute-Never,特權模式不可執行。在支持的情況下,PXN 位決定處理器在 PL1 特權級時是否可執行該內存區域的代碼。如果執行,會觸發 Permission fault
  • NS bit
    • NS = Non-Secure,只有在 啟用了 TrustZone 安全擴展 的系統中才有意義
    • 只會出現在一級頁表項中
    • 詳見 3.4 章節
  • Domain
    • Domains 是一種粗粒度的內存訪問控制機制,屬于 ARMv7 MMU 的一部分
    • 詳見 3.3 章節
  • AP[2], AP[1:0]
    • 訪問權限位
    • 詳見 3.5 章節
  • S bit
    • 可共享位。確定尋址區域是否為可共享內存
  • nG bit
    • 非全局位。確定如何在 TLB 中標記轉換
  • Bit[18], when bits[1:0] indicate a Section or Supersection descriptor
    • 0 Descriptor is for a Section.
    • 1 Descriptor is for a Supersection

3.3 Domains

在 ARMv7 的 VMSA 中,僅存在于 Short-descriptor format:

  • 總共有 16 個 Domains(編號 0 ~ 15)
  • 每個 Section/Page 在頁表中被指定屬于哪個 Domain(用 4-bit 域表示)(不支持 Supersections)
  • 僅有一級頁表條目有 Domains 域,二級轉換表條目從父級一級頁表條目繼承域設置
  • CPU 中有一個 DACR(Domain Access Control Register),用于配置每個 Domain 的訪問權限

DACR 是一個 32-bit 寄存器,每兩個 bit 控制一個 domain 的訪問權限。每個域的訪問權限可以設置為:
在這里插入圖片描述

含義解釋
0b00No access訪問該 domain 的內存會導致 fault
0b01Client根據頁表中的 AP bits 進行訪問權限檢查(標準權限檢查方式)
0b11Manager不檢查訪問權限,直接允許訪問(相當于繞過 AP 位)
0b10保留(未使用)訪問該 domain 的內存會導致 fault

? 工作原理總結

訪問內存時,ARM 的 MMU 進行如下步驟:

  • 使用頁表將虛擬地址翻譯為物理地址,并獲得該頁屬于哪個 Domain(通過頁表項的 bits[8:5])
  • 查 DACR 寄存器,查看該 Domain 的訪問權限設定
  • 根據權限設定決定行為:
    • No access → 拋出異常
    • Client → 繼續檢查頁表中的 AP(Access Permission)位
    • Manager → 直接允許訪問(忽略 AP 位)

Domains 域現在不常用了。粒度太粗、管理復雜,ARMv8+ 已廢棄 Domain 概念,使用更加細粒度的 EL 權限模型、權限表(PTE) 來管理訪問權限。
在 ARMv7 的 VMSA 中,Domains 是 MMU 機制的一部分,不能繞過或關閉。通常可以將所有 domain 設置為 Client,配合合理的頁表 AP

3.4 Control of Secure or Non-secure memory access

??在 ARMv7 架構中,針對安全(Secure)與非安全(Non-secure)PL1&0 階段的第 1 階段地址轉換,其轉換表基址寄存器(TTBR0、TTBR1)及轉換表基址控制寄存器(TTBCR)均采用安全與非安全 雙 bank 設計。處理器執行內存訪問時所處的安全狀態(Security state)將自動選擇對應版本的寄存器組。

??當 CPU 當前處于 Secure 狀態(比如運行 Secure OS 或 TrustZone secure world 中的代碼)時:頁表中設置的 NS 位會影響這個訪問是訪問到:

  • Secure memory(NS=0)
  • 還是 Non-Secure memory(NS=1)

??當 CPU 當前處于 Non-secure 狀態時(比如運行 Linux 等普通 OS):頁表中設置的 NS 位根本不起作用,會被忽略。因為 Non-secure world 不能訪問 Secure memory。

3.5 Memory access control

??在轉換表描述符中,訪問權限位(Access permission bits)用于控制對相應內存區域的訪問。短描述符轉換表格式(Short-descriptor translation table format)支持兩種定義訪問權限的方式:

  • 三比特模式(AP[2:0]):
    • 通過 AP[2:0] 三個比特位定義訪問權限。
  • 兩比特模式(AP[2:1] + 訪問標志位):
    • 通過 AP[2:1] 兩個比特位定義訪問權限,
    • AP[0] 可作為訪問標志(Access flag)使用。

系統控制寄存器 SCTLR.AFE 用于選擇訪問權限的配置模式:

  • 若將該位置 1(啟用訪問標志功能),則自動選擇 AP[2:1] 定義訪問權限的模式。

在這里插入圖片描述
在這里插入圖片描述

3.6 轉換表的工作流程

以 Small page 為例:

  • 首先輸入的虛擬地址,可以大致分為 3 部分
    • 一級頁表偏移
    • 二級頁表偏移
    • 頁內偏移
  • 根據輸入地址的范圍(也就是 N 的取值,這一塊可以先閱讀第 4 章節),找到頁表基址(TTBR0 or TTBR1)
  • 根據 頁表基址 + 一級頁表偏移,找到一級頁表的 “頁表項描述符”
    • 頁表基址,實際上就是 PGD 表的基址;一級頁表偏移指的就是 PGD 表中的某個頁表項
  • 根據上一步找到 “一級頁表項描述符” 中包含的二級頁表的基址[31:10] + 二級頁表偏移,找到二級頁表的 “頁表項描述符”
    • 二級頁表的基址,實際上就是 PTE 表的基址;二級頁表偏移指的就是 PTE 表中的某個頁表項
  • 在二級頁表項描述符中,就包含了最終的物理地址的 [31:12],再加上 頁內偏移,最終就找到了物理地址
    • 二級頁表項描述符,指的就是 PTE 表中的某個頁表項

在這里插入圖片描述

4、關于頁表基址 TTBR

?? ARMv7 中,有兩個存放頁表(一級頁表)基地址的寄存器,TTBR0 和 TTBR1。那 MMU 進行地址翻譯(translation table walk)的時候到底是選擇哪一個寄存器的值作為基地址呢?

?? 那么這個時候就需要用到 TTBCR 寄存器,這個寄存器的格式如下圖:
在這里插入圖片描述

其中最后 [2-0] 位(值為N)決定了用 TTBR0 還是 TTBR1,手冊里面已經說的很清楚了,這里直接貼出來。

在這里插入圖片描述

  • 當 N == 0 是,使用 TTBR0 作為基址。關閉/禁用 TTBR1 作為基址
  • 當 N>0 時,如果給的虛擬地址 [31:32-N] 位都是 0,那么用 TTBR0,一旦這其中的某一位是 1 了,就用 TTBR1。
    • 又因為 N 的取值只有 0 到 7,所以每個 N 的取值都對應了一個用 TTBR0 或者 TTBR1 的分界線。
    • 比如當 N=2 時,如果給的虛擬地址第 [31:30] 位為 0,即不超過 0x40000000,那么就用 TTBR0,一旦等于或者超過 0x40000000 那么就用 TTBR1。

下面還有一張 N 值不同時,對應的地址分界線圖:
在這里插入圖片描述
在這里插入圖片描述

理論上,一個頁表基址寄存器也是能正常工作,但兩個可以帶來更好的性能和系統隔離性

為什么需要兩個基址寄存器?

? 好處 1:用戶態和內核態分離

  • 操作系統常常希望:
    • 用戶進程只能訪問用戶空間
    • 內核可以訪問全部空間
  • 有了兩個頁表:
    • 切換進程時,只需切換 TTBR0(用戶空間部分)
    • TTBR1(內核空間頁表)不變,提升性能并確保安全

? 好處 2:性能優化

  • 避免頻繁清空整個 TLB(Translation Lookaside Buffer):
    • 進程切換時只換 TTBR0 → 用戶空間換了
    • TTBR1 → 內核頁表不變,TLB 中內核頁表項仍有效

? 好處 3:內核共享

  • 所有進程共用一套內核映射(TTBR1)
  • 每個進程擁有自己獨立的用戶映射(TTBR0)

5、關于內存屬性

??除輸出地址外,指向內存頁或區域的頁表項通常還包含用于定義目標內存屬性字段(Memory Region Attribute Fields),用于控制:

  • 內存類型(如Normal或Device內存)
  • 緩存訪問策略(如Write-Back、Write-Through)
  • 可共享性(Shareable),即多核間的一致性(Coherency)

為深入理解內存屬性字段的配置機制,需區分以下兩種模式:

  • Short-descriptor translation table format (無 TEX 重映射)
    • 直接通過TEX[2:0]、C(Cacheable)、B(Bufferable)位組合定義內存行為,適用于大多數標準場景
  • Short-descriptor translation table format (啟用 TEX 重映射)
    • 通過 CP15 寄存器重映射 TEX 位語義,支持更靈活的緩存策略定制,常見于定制化硬件設計

5.1 without TEX remap

??使用 Short-descriptor translation table formats 時,可以使用 SCTLR.TRE 禁用 TEX 重映射。

在這里插入圖片描述
在這里插入圖片描述
(without TEX remap 場景下的的 Shareability 與 S 位)

轉換表項(Translation Table Entry)中還包含一個 S 位(Shareable位),其作用如下:

  • 若表項指向設備內存(Device)或強序內存(Strongly-ordered):
    • 該位被忽略(無論S=0或1均不生效)
  • 若表項指向普通內存(Normal Memory):
    • 該位決定內存區域的可共享性(Shareability):
      • S=0:該普通內存區域為非可共享(Non-shareable)。
      • S=1:該普通內存區域為可共享(Shareable)

在這里插入圖片描述

5.2 with TEX remap

??當使用短描述符轉換表格式(Short-descriptor translation table formats)時,若將系統控制寄存器 SCTLR.TRE 置為1,則啟用 TEX 重映射(TEX Remap)功能。在此配置下:

  • 軟件配置要求
    • 定義轉換表的軟件必須編程設置 PRRR(Primary Region Remap Register)和 NMRR(Normal Memory Remap Register),以指定 7 種可能的內存區域屬性
    • 轉換表描述符中的 TEX[0]、C(Cacheable)和 B(Bufferable)位通過索引 PRRRNMRR 來定義內存區域屬性。
    • 硬件不會使用 TEX[2:1] 位
  • TEX 重映射生效時的行為
    • 對于 TEX[0]、C 和 B 位的 8 種可能組合中的 7 種,其內存區域屬性由 PRRRNMRR 的字段定義(如本節所述)。
    • 第 8 種組合的含義由具體實現定義(IMPLEMENTATION DEFINED)。
    • PRRR 中的 4 個比特位用于定義該區域是否可共享(Shareable)
  • 寄存器字段映射關系
    • 對于轉換表項中TEX[0]、C 和 B 位的所有可能編碼組合,表 B3-12 列出了 PRRRNMRR 寄存器中描述內存區域屬性的對應字段。

在這里插入圖片描述

?? 乍一看,有點迷糊,這塊不太好理解。總結來說,在 without TEX remap 的場景下,在 PTE 中直接寫 TEX[2:0]、B、C 對應的 bit 位,就可以控制頁面的內存區域屬性。但是在 with TEX remap 情況下,TEX、B、C 的值不再直接表示區域內存屬性。所有可能的區域內存屬性的值,都在 PRRRNMRR 寄存器中。而 TEX、B、C 三個 bit 組成的 8 種組合,就是在這兩個寄存器中的偏移。

PRRR, Primary Region Remap Register, VMSA

在這里插入圖片描述
所有 TEX[0]、B、C 可能的組合如下:對應的 n 是幾,使用的就是 NOS(n) + TR(n) 的組合
在這里插入圖片描述
關于 PRRR 寄存器,以下是詳細解釋:
在這里插入圖片描述

  • NOS 位是控制內存區域的可共享屬性。
  • NS1, bit[19]
  • NS0, bit[18]
  • DS1, bit[17]
  • DS0, bit[16]

?? 這些位控制的是 Normal memory 和 Device memory 的共享性(shareability)屬性,也就是說,它們不是控制緩存方式,而是控制 是否是 shareable 類型。

名稱控制的情況含義(bit 值)
NS0 (bit 18)頁表中 S=0 且是 Normal memory0 ? Non-shareable
1 ? Shareable
NS1 (bit 19)頁表中 S=1 且是 Normal memory0 ? Non-shareable
1 ? Shareable
NS0 (bit 18)類似邏輯,作用于 Device memory,S=0/S=1 映射

在這里插入圖片描述

  • TR 位用于控制內存類型

NMRR, Normal Memory Remap Register, VMSA

關于 NMRR 寄存器這里不再詳細解釋了,有興趣的可以自行研究。
在這里插入圖片描述

6、Linux 下的頁表映射

??上面講的都是理論,我們對應到 Linux 源碼中,看看內存區域屬性是如何配置的。我們查看下 ARM 架構下的 ioremap 的實現。

arch\arm\mm\ioremap.c:

void __iomem *ioremap(resource_size_t res_cookie, size_t size)
{return arch_ioremap_caller(res_cookie, size, MT_DEVICE,__builtin_return_address(0));
}
EXPORT_SYMBOL(ioremap);void __iomem *ioremap_cache(resource_size_t res_cookie, size_t size)
{return arch_ioremap_caller(res_cookie, size, MT_DEVICE_CACHED,__builtin_return_address(0));
}
EXPORT_SYMBOL(ioremap_cache);void __iomem *ioremap_wc(resource_size_t res_cookie, size_t size)
{return arch_ioremap_caller(res_cookie, size, MT_DEVICE_WC,__builtin_return_address(0));
}

??關于 MT_DEVICEMT_DEVICE_CACHEDMT_DEVICE_WC,就是內存區域屬性,最終會被翻譯成我們在第 5 章節所講的 TEX、B、C 的值寫到 PTE 頁表項中。

arch\arm\mm\mmu.c

static struct mem_type mem_types[] __ro_after_init = {[MT_DEVICE] = {		  /* Strongly ordered / ARMv6 shared device */.prot_pte	= PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED |L_PTE_SHARED,.prot_l1	= PMD_TYPE_TABLE,.prot_sect	= PROT_SECT_DEVICE | PMD_SECT_S,.domain		= DOMAIN_IO,},[MT_DEVICE_NONSHARED] = { /* ARMv6 non-shared device */.prot_pte	= PROT_PTE_DEVICE | L_PTE_MT_DEV_NONSHARED,.prot_l1	= PMD_TYPE_TABLE,.prot_sect	= PROT_SECT_DEVICE,.domain		= DOMAIN_IO,},[MT_DEVICE_CACHED] = {	  /* ioremap_cache */.prot_pte	= PROT_PTE_DEVICE | L_PTE_MT_DEV_CACHED,.prot_l1	= PMD_TYPE_TABLE,.prot_sect	= PROT_SECT_DEVICE | PMD_SECT_WB,.domain		= DOMAIN_IO,},[MT_DEVICE_WC] = {	/* ioremap_wc */.prot_pte	= PROT_PTE_DEVICE | L_PTE_MT_DEV_WC,.prot_l1	= PMD_TYPE_TABLE,.prot_sect	= PROT_SECT_DEVICE,.domain		= DOMAIN_IO,},............
}

上表中提到的 L_PTE_MT_DEV_SHAREDL_PTE_SHARED 頁表項 PTE 相關的宏,定義在 pgtable-2level.h 頭文件中:

arch\arm\include\asm\pgtable-2level.h

/** These are the memory types, defined to be compatible with* pre-ARMv6 CPUs cacheable and bufferable bits: n/a,n/a,C,B* ARMv6+ without TEX remapping, they are a table index.* ARMv6+ with TEX remapping, they correspond to n/a,TEX(0),C,B** MT type		Pre-ARMv6	ARMv6+ type / cacheable status* UNCACHED		Uncached	Strongly ordered* BUFFERABLE		Bufferable	Normal memory / non-cacheable* WRITETHROUGH		Writethrough	Normal memory / write through* WRITEBACK		Writeback	Normal memory / write back, read alloc* MINICACHE		Minicache	N/A* WRITEALLOC		Writeback	Normal memory / write back, write alloc* DEV_SHARED		Uncached	Device memory (shared)* DEV_NONSHARED	Uncached	Device memory (non-shared)* DEV_WC		Bufferable	Normal memory / non-cacheable* DEV_CACHED		Writeback	Normal memory / write back, read alloc* VECTORS		Variable	Normal memory / variable** All normal memory mappings have the following properties:* - reads can be repeated with no side effects* - repeated reads return the last value written* - reads can fetch additional locations without side effects* - writes can be repeated (in certain cases) with no side effects* - writes can be merged before accessing the target* - unaligned accesses can be supported** All device mappings have the following properties:* - no access speculation* - no repetition (eg, on return from an exception)* - number, order and size of accesses are maintained* - unaligned accesses are "unpredictable"*/
#define L_PTE_MT_UNCACHED	(_AT(pteval_t, 0x00) << 2)	/* 0000 */
#define L_PTE_MT_BUFFERABLE	(_AT(pteval_t, 0x01) << 2)	/* 0001 */
#define L_PTE_MT_WRITETHROUGH	(_AT(pteval_t, 0x02) << 2)	/* 0010 */
#define L_PTE_MT_WRITEBACK	(_AT(pteval_t, 0x03) << 2)	/* 0011 */
#define L_PTE_MT_MINICACHE	(_AT(pteval_t, 0x06) << 2)	/* 0110 (sa1100, xscale) */
#define L_PTE_MT_WRITEALLOC	(_AT(pteval_t, 0x07) << 2)	/* 0111 */
#define L_PTE_MT_DEV_SHARED	(_AT(pteval_t, 0x04) << 2)	/* 0100 */
#define L_PTE_MT_DEV_NONSHARED	(_AT(pteval_t, 0x0c) << 2)	/* 1100 */
#define L_PTE_MT_DEV_WC		(_AT(pteval_t, 0x09) << 2)	/* 1001 */
#define L_PTE_MT_DEV_CACHED	(_AT(pteval_t, 0x0b) << 2)	/* 1011 */
#define L_PTE_MT_VECTORS	(_AT(pteval_t, 0x0f) << 2)	/* 1111 */
#define L_PTE_MT_MASK		(_AT(pteval_t, 0x0f) << 2)

對于 ARMv7 來說,我們只需要關注上表的后三位。分別對應的就是 TEX[0]、C、B。

6.1 關于 PRRR 和 NMRR 寄存器的值

arch\arm\mm\proc-v7-2level.S

	/** Memory region attributes with SCTLR.TRE=1**   n = TEX[0],C,B*   TR = PRRR[2n+1:2n]		- memory type*   IR = NMRR[2n+1:2n]		- inner cacheable property*   OR = NMRR[2n+17:2n+16]	- outer cacheable property**			n	TR	IR	OR*   UNCACHED		000	00*   BUFFERABLE		001	10	00	00*   WRITETHROUGH	010	10	10	10*   WRITEBACK		011	10	11	11*   reserved		110*   WRITEALLOC		111	10	01	01*   DEV_SHARED		100	01*   DEV_NONSHARED	100	01*   DEV_WC		001	10*   DEV_CACHED		011	10** Other attributes:**   DS0 = PRRR[16] = 0		- device shareable property*   DS1 = PRRR[17] = 1		- device shareable property*   NS0 = PRRR[18] = 0		- normal shareable property*   NS1 = PRRR[19] = 1		- normal shareable property*   NOS = PRRR[24+n] = 1	- not outer shareable*/
.equ	PRRR,	0xff0a81a8        // 初始化了 PRRR 寄存器的值
.equ	NMRR,	0x40e040e0        // 初始化了 NMRR 寄存器的值

對應的,將 PRRR 翻譯結果如下:

寄存器偏移對應值屬性
TR000Strongly-ordered
TR110Normal memory
TR210Normal memory
TR310Normal memory
TR401Device
TR500Strongly-ordered
TR600Strongly-ordered
TR710Normal memory
寄存器偏移對應值屬性
NOS01Memory region is Inner Shareable
NOS11Memory region is Inner Shareable
NOS21Memory region is Inner Shareable
NOS31Memory region is Inner Shareable
NOS41Memory region is Inner Shareable
NOS51Memory region is Inner Shareable
NOS61Memory region is Inner Shareable
NOS71Memory region is Inner Shareable

NMRR 翻譯結果如下:

寄存器偏移對應值屬性
OR000Region is Non-cacheable
OR100Region is Non-cacheable
OR201Region is Write-Back, Write-Allocate
OR311Region is Write-Back, no Write-Allocate
OR400Region is Non-cacheable
OR500Region is Non-cacheable
OR600Region is Non-cacheable
OR701Region is Write-Back, Write-Allocate

6.2 案例

函數調用關系鏈:

ioremap+-> arch_ioremap_caller+-> __arm_ioremap_pfn_caller+-> get_mem_type+-> ioremap_page_range+-> kmsan_ioremap_page_range+->__vmap_pages_range_noflush+->vmap_range_noflush+->vmap_p4d_range+->vmap_pud_range+->vmap_pmd_range+->vmap_pte_range+->set_pte_at+->cpu_v7_set_pte_ext

總結來說,在 ARMv7 架構下,調用 ioremap 接口,默認會將地址映射成 MT_DEVICE 屬性。

void __iomem *ioremap(resource_size_t res_cookie, size_t size)
{return arch_ioremap_caller(res_cookie, size, MT_DEVICE,__builtin_return_address(0));
}
EXPORT_SYMBOL(ioremap);

在調用 get_mem_type 接口時,會將 MT_DEVICE 轉換成架構相關的內存區域屬性相關的宏 :
PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED | L_PTE_SHARED

static struct mem_type mem_types[] __ro_after_init = {[MT_DEVICE] = {		  /* Strongly ordered / ARMv6 shared device */.prot_pte	= PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED |L_PTE_SHARED,.prot_l1	= PMD_TYPE_TABLE,.prot_sect	= PROT_SECT_DEVICE | PMD_SECT_S,.domain		= DOMAIN_IO,},............
}const struct mem_type *get_mem_type(unsigned int type)
{return type < ARRAY_SIZE(mem_types) ? &mem_types[type] : NULL;
}

而這其中,和硬件相關的就是 PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED

#define L_PTE_SHARED		(_AT(pteval_t, 1) << 10)	/* shared(v6), coherent(xsc3) */#define L_PTE_MT_DEV_SHARED	(_AT(pteval_t, 0x04) << 2)	/* 0100 */

L_PTE_MT_DEV_SHARED 宏為例,介紹如何通過這個宏,去控制內存區域屬性的。

  • 對應的 TEX[0]、C、B,組成的 index 值為 0x4
  • 對應 RRRNMRR 寄存器中,使用的就是
    • NOS4 + TR4 + OR4 + IR4 的組合、

最終,會調用 set_pte_at 接口,將這些宏設置到 PTE 頁表項中。
該函數的三個入參:

  • r0: pte 頁表項的地址
  • r1: Linux 版二級頁表項的內容(也就是 PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED | L_PTE_SHARED 這些值的組合)
  • r2: 通常是 0,除非需要特殊設置
  • 輸出 r3:硬件頁表項(詳情見 6.3 章節)
ENTRY(cpu_v7_set_pte_ext)
#ifdef CONFIG_MMUstr	r1, [r0]			@ linux version //把 r1(linux 的 PTE)存入 r0 地址bic	r3, r1, #0x000003f0         //先清除 r1 的 bit[9:4](通常為 memory type / cache屬性)bic	r3, r3, #PTE_TYPE_MASK      //然后再清除最低 2 位(bit[1:0],即頁表項類型 Small Page / Section)orr	r3, r3, r2					//將擴展屬性(ext)合并進 r3orr	r3, r3, #PTE_EXT_AP0 | 2    //設置 AP0 位(Access permission)和最低兩位為 0b10,表示 Small Pagetst	r1, #1 << 4                 //如果原始 PTE 的 bit4 被置位,則設置硬件頁表項的 TEX=1orrne	r3, r3, #PTE_EXT_TEX(1)eor	r1, r1, #L_PTE_DIRTYtst	r1, #L_PTE_RDONLY | L_PTE_DIRTYorrne	r3, r3, #PTE_EXT_APX	//根據頁表中的只讀和臟頁標志,設置 APX(Access Permission Extension)位tst	r1, #L_PTE_USERorrne	r3, r3, #PTE_EXT_AP1	//如果頁表設置了 user-mode 訪問權限,設置 AP1(即允許用戶空間訪問)tst	r1, #L_PTE_XNorrne	r3, r3, #PTE_EXT_XN		//設置執行禁止位(XN)tst	r1, #L_PTE_YOUNGtstne	r1, #L_PTE_VALIDeorne	r1, r1, #L_PTE_NONEtstne	r1, #L_PTE_NONEmoveq	r3, #0					//這段邏輯是做 PTE 的“有效性”判斷:如果不是 Young 或 Valid,或者被標記為 None(無效項),則把 r3 清零(頁表項無效)ARM(	str	r3, [r0, #2048]! )		//真正的“硬件頁表項”是寫在 r0 + 2048 字節偏移處!THUMB(	add	r0, r0, #2048 )THUMB(	str	r3, [r0] )ALT_SMP(W(nop))ALT_UP (mcr	p15, 0, r0, c7, c10, 1)		@ flush_pte
#endifbx	lr
ENDPROC(cpu_v7_set_pte_ext)

6.3 硬件頁表項和軟件頁表項

??在 ARM 架構下,尤其是 Linux 運行于 ARMv6/v7 等 MMU 支持的平臺時,頁表(Page Table)是內存管理的核心組件。為了兼顧內核的抽象管理與硬件 MMU 的訪問需求,Linux 使用了“雙版本頁表”的設計思路,即:

  • 軟件版本(Software Version)
  • 硬件版本(Hardware Version)

??這兩者雖共享數據結構和邏輯關聯,但服務于不同的對象和目的,理解它們的區別對于調試頁表異常、實現頁表擴展、或閱讀內核代碼都至關重要。

軟件版本頁表(Software Version)

軟件版本是 Linux 內核自己維護和使用的頁表項,它主要用于:

  • 內核自身管理的權限與狀態位(如 Dirty、Accessed、Writeback)
  • Linux 的抽象內存屬性(如 L_PTE_DIRTY, L_PTE_YOUNG, L_PTE_RDONLY 等)
  • 與用戶空間接口(如 mprotect, mmap)之間的語義協同

這些軟件位通常嵌入在標準頁表項結構中(如 pteval_t),但并不一定直接被 ARM MMU 硬件識別。在 ARM Linux 中,這些位通過宏定義編碼進 PTE 中,僅用于 Linux 內核邏輯判斷,硬件則會忽略它們

軟件頁表項位通常保存在:

  • pte_t 類型的結構體中
  • 頁表內存中的 [r0] 位置(見 cpu_v7_set_pte_ext 中第一條 str r1, [r0])

硬件版本頁表(Hardware Version)

硬件版本是寫入到 ARM MMU 實際訪問的頁表內存地址 中的內容。它必須符合 ARM 架構定義的格式,包含如下內容:

  • 頁表項類型(small page, section, supersection 等)
  • 權限位(AP[2:0], APX)
  • 緩存控制位(TEX[2:0], C, B)
  • 共享屬性(S, nG 等)
  • 執行權限(XN)

其實就是我們上面所講到的一些 ARM 寄存器相關的知識

在執行頁表設置時,Linux 會將軟件版本的頁表項翻譯成硬件格式并寫入對應物理地址供 MMU 使用。

硬件頁表項最終保存在:

  • 頁表物理頁中,通常偏移 +2048 字節
  • 通過 cpu_v7_set_pte_ext 函數中 str r3, [r0, #2048]! 寫入

arch\arm\include\asm\pgtable-2level.h 中的宏,對于硬件版本和軟件版本有很好的體現:

/** "Linux" PTE definitions.** We keep two sets of PTEs - the hardware and the linux version.* This allows greater flexibility in the way we map the Linux bits* onto the hardware tables, and allows us to have YOUNG and DIRTY* bits.** The PTE table pointer refers to the hardware entries; the "Linux"* entries are stored 1024 bytes below.*/
#define L_PTE_VALID		(_AT(pteval_t, 1) << 0)		/* Valid */
#define L_PTE_PRESENT		(_AT(pteval_t, 1) << 0)
#define L_PTE_YOUNG		(_AT(pteval_t, 1) << 1)
#define L_PTE_DIRTY		(_AT(pteval_t, 1) << 6)
#define L_PTE_RDONLY		(_AT(pteval_t, 1) << 7)
#define L_PTE_USER		(_AT(pteval_t, 1) << 8)
#define L_PTE_XN		(_AT(pteval_t, 1) << 9)
#define L_PTE_SHARED		(_AT(pteval_t, 1) << 10)	/* shared(v6), coherent(xsc3) */
#define L_PTE_NONE		(_AT(pteval_t, 1) << 11)//以下都是硬件版本,上面都講過
/** These are the memory types, defined to be compatible with* pre-ARMv6 CPUs cacheable and bufferable bits: n/a,n/a,C,B* ARMv6+ without TEX remapping, they are a table index.* ARMv6+ with TEX remapping, they correspond to n/a,TEX(0),C,B** MT type		Pre-ARMv6	ARMv6+ type / cacheable status* UNCACHED		Uncached	Strongly ordered* BUFFERABLE		Bufferable	Normal memory / non-cacheable* WRITETHROUGH		Writethrough	Normal memory / write through* WRITEBACK		Writeback	Normal memory / write back, read alloc* MINICACHE		Minicache	N/A* WRITEALLOC		Writeback	Normal memory / write back, write alloc* DEV_SHARED		Uncached	Device memory (shared)* DEV_NONSHARED	Uncached	Device memory (non-shared)* DEV_WC		Bufferable	Normal memory / non-cacheable* DEV_CACHED		Writeback	Normal memory / write back, read alloc* VECTORS		Variable	Normal memory / variable** All normal memory mappings have the following properties:* - reads can be repeated with no side effects* - repeated reads return the last value written* - reads can fetch additional locations without side effects* - writes can be repeated (in certain cases) with no side effects* - writes can be merged before accessing the target* - unaligned accesses can be supported** All device mappings have the following properties:* - no access speculation* - no repetition (eg, on return from an exception)* - number, order and size of accesses are maintained* - unaligned accesses are "unpredictable"*/
#define L_PTE_MT_UNCACHED	(_AT(pteval_t, 0x00) << 2)	/* 0000 */
#define L_PTE_MT_BUFFERABLE	(_AT(pteval_t, 0x01) << 2)	/* 0001 */
#define L_PTE_MT_WRITETHROUGH	(_AT(pteval_t, 0x02) << 2)	/* 0010 */
#define L_PTE_MT_WRITEBACK	(_AT(pteval_t, 0x03) << 2)	/* 0011 */
#define L_PTE_MT_MINICACHE	(_AT(pteval_t, 0x06) << 2)	/* 0110 (sa1100, xscale) */
#define L_PTE_MT_WRITEALLOC	(_AT(pteval_t, 0x07) << 2)	/* 0111 */
#define L_PTE_MT_DEV_SHARED	(_AT(pteval_t, 0x04) << 2)	/* 0100 */
#define L_PTE_MT_DEV_NONSHARED	(_AT(pteval_t, 0x0c) << 2)	/* 1100 */
#define L_PTE_MT_DEV_WC		(_AT(pteval_t, 0x09) << 2)	/* 1001 */
#define L_PTE_MT_DEV_CACHED	(_AT(pteval_t, 0x0b) << 2)	/* 1011 */
#define L_PTE_MT_VECTORS	(_AT(pteval_t, 0x0f) << 2)	/* 1111 */
#define L_PTE_MT_MASK		(_AT(pteval_t, 0x0f) << 2)

為什么需要兩個版本?

  • 可擴展性:軟件頁表項可以攜帶更多信息,如“是否訪問過”、“是否臟”等,硬件頁表項不一定有足夠位支持這些信息
  • 兼容性:Linux 在不同架構上運行時,仍可以通過抽象的軟件表示層實現統一邏輯
  • 性能調優:部分頁表標志如 young/old, dirty 可由軟件策略控制,而不依賴硬件

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/915313.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/915313.shtml
英文地址,請注明出處:http://en.pswp.cn/news/915313.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Github 貪吃蛇 主頁設置

自動化腳本頂部元信息觸發條件&#xff08;on:&#xff09;作業&#xff08;jobs:&#xff09;步驟&#xff08;steps:&#xff09;1. 生成 SVG2. 推送到 output 分支Commit & Push在 README 里引用參考&#xff1a;https://github.com/Platane/Platane/tree/master 首先寫…

關于Spring RestTemplate

? 一、概述RestTemplate 是 Spring Framework 提供的一個同步 HTTP 客戶端工具&#xff0c;用于簡化與 RESTful API 的交互。它封裝了底層 HTTP 通信細節&#xff0c;提供了統一的 API 來發送各種 HTTP 請求&#xff08;GET、POST、PUT、DELETE 等&#xff09;&#xff0c;并自…

異步解決一切問題 |消息隊列 |減少嵌套 |hadoop |rabbitmq |postsql

設計準則“為什么要考慮這個問題”The forward logic is only about 10% of your code, everything else is 90%.主流邏輯 10%保障擴容和穩健的代碼設計90%同步代碼就是綁在一個繩上的螞蚱異步就是實現了解耦這個異步或許有點類似于--一些分布式數據的處理 設計如何實現的呢?…

Spring AI 項目實戰(十八):Spring Boot + AI + Vue3 + OSS + DashScope 實現高效語音識別系統(附完整源碼)

系列文章 序號 文章名稱 1 Spring AI 項目實戰(一):Spring AI 核心模塊入門 2 Spring AI 項目實戰(二):Spring Boot + AI + DeepSeek 深度實戰(附完整源碼) 3 Spring AI 項目實戰(三):Spring Boot + AI + DeepSeek 打造智能客服系統(附完整源碼) 4

指針數組和數組指針的應用案例

1. 指針數組應用&#xff1a;查找最長字符串用指針數組存儲若干字符串&#xff0c;編寫函數找出其中最長的字符串&#xff08;若有多個&#xff0c;返回第一個&#xff09;。#include <stdio.h> #include <string.h>// 函數原型&#xff1a;找出最長字符串 const c…

MCU進入低功耗模式前的引腳處理原則和方法 --> 以最小化低功耗電流

在MCU進入低功耗模式(如Sleep, Stop, Standby, Deep Sleep等)前,精心處理每一個GPIO引腳的狀態是最大限度降低功耗電流的關鍵一步。懸空或配置不當的引腳是導致“漏電”的常見原因。以下是處理引腳以達到最小低功耗電流的原則和方法: ?? 核心原則 避免浮空輸入: 浮空(…

張 關于大語言模型(LLM)置信度研究的經典與前沿論文 :溫度縮放;語義熵;自一致性;事實與反思;檢索增強;黑盒引導;

關于大語言模型(LLM)置信度研究的經典與前沿論文 :溫度縮放;語義熵;自一致性;事實與反思;檢索增強;黑盒引導; 目錄 關于大語言模型(LLM)置信度研究的經典與前沿論文 :溫度縮放;語義熵;自一致性;事實與反思;檢索增強;黑盒引導; 一、校準方法:讓模型概率更貼近真實正確…

ICT測試原理之--什么是假短

ICT測試原理之–什么是假短 文章目錄ICT測試原理之--什么是假短一、假短的由來防止假短二、無法檢測的短路示例解決無法檢測的短路調試短路文件調試意外斷路調試意外短路三、調試假短報告短路和斷路報告假短報告短路設備/引腳功能性短路測試功能性短路測試的語法一、假短的由來…

三種深度學習模型(LSTM、CNN-LSTM、貝葉斯優化的CNN-LSTM/BO-CNN-LSTM)對北半球光伏數據進行時間序列預測

代碼功能 該代碼實現了一個光伏發電量預測系統&#xff0c;采用三種深度學習模型&#xff08;LSTM、CNN-LSTM、貝葉斯優化的CNN-LSTM&#xff09;對北半球光伏數據進行時間序列預測&#xff0c;并通過多維度評估指標和可視化對比模型性能。 算法步驟 1. 數據預處理 數據導入&am…

Typecho+阿里云CDN完整配置:防止DDoS攻擊與IP暴露

文章目錄 Typecho使用阿里云CDN保護網站真實IP地址的完整指南 背景與問題分析 技術選型與方案設計 詳細實施步驟 第一步:阿里云CDN基礎配置 第二步:DNS解析設置 第三步:源站服務器防護配置 Nginx服務器配置 防火墻配置(以Ubuntu為例) 第四步:Typecho配置調整 高級防護措施…

[硬件]運算放大器對相位噪聲的影響與設計提示

運算放大器對相位噪聲的影響與設計提示 文章目錄運算放大器對相位噪聲的影響與設計提示運放影響位噪聲的主要因素如何最小化運放對相位噪聲的影響總結運算放大器是常用的模擬電路元器件&#xff0c;通常用于放大信號&#xff0c;增強驅動。但是當使用運放放大一個信號時&#x…

github jekyll+chirpy主題搭建博客

github jekyllchirpy主題搭建博客 標簽&#xff1a;后端、blog、jekyll 全文鏈接 本文簡要介紹了如何基于 GitHub Pages、Jekyll 及 Chirpy 主題搭建個人博客的流程和注意事項。 主要內容 GitHub Pages 站點簡介 可免費搭建個人博客&#xff0c;支持自定義域名&#xff0c;適…

Flutter狀態管理篇之ValueNotifier(三)

目錄 前言 一、ValueNotifier 概述 二、ValueNotifier 的實現原理 1.類定義 1.類定義 2.關鍵字段 3.關鍵方法 1.構造函數 2.getter:value 3.setter:value: 4.toString 2.繼承自ChangeNotifier的機制 3.ValueListenable 接口 三、ValueNotifier 的用法 1.基本用法…

Ubuntu togo 系統安裝指南

制作一個 “Ubuntu To Go” 系統&#xff08;也就是一個可以隨身攜帶、在不同電腦上啟動并擁有持久化存儲的U盤系統&#xff09;是解決你問題的完美方案。 這樣一來&#xff0c;你就可以&#xff1a; 不改動你現有的電腦系統 (保留你的Ubuntu 20.04 或 Windows)。擁有一個完整…

Python爬蟲實戰:研究pefile庫相關技術

一、引言 可執行文件(Portable Executable,PE)是 Windows 操作系統中最常見的文件格式,包括.exe、.dll、.sys 等多種類型。對 PE 文件的分析在軟件逆向工程、惡意軟件檢測、系統安全研究等領域具有重要意義。傳統的 PE 文件分析主要依賴手動操作和專業工具,效率較低且對分…

盟接之橋說制造:差異化定位與效率競爭的雙輪驅動

在當今競爭日益激烈的商業環境中&#xff0c;企業如何在市場中脫穎而出&#xff0c;既避免陷入同質化的價格戰&#xff0c;又能夠通過效率提升實現可持續發展&#xff0c;是每一個經營者必須思考的問題。本文將圍繞“差異化”與“效率競爭”兩大核心戰略展開分析&#xff0c;探…

Vue基礎(前端教程①-路由)

項目結構src/├── router/│ └── index.js # 路由配置├── components/│ ├── Home.vue # 首頁組件│ ├── About.vue # 關于頁組件│ └── Contact.vue # 聯系頁組件├── App.vue # 根組件&#xff08;含導航欄&…

駕馭 Spring Boot 事件機制:8 個內置事件 + 自定義擴展實戰

駕馭 Spring Boot 事件機制&#xff1a;8 個內置事件 自定義擴展實戰在 Spring Boot 應用的完整生命周期中&#xff0c;框架為我們預埋了 8 個關鍵事件&#xff08;Application-level & Context-level&#xff09;。 理解并善用這些事件&#xff0c;可以在“不侵入框架、…

【kafka4源碼學習系列】kafka4總體架構介紹

二 kafka架構介紹學習一個系統之前很重要的一點就是先了解這個系統整體的架構&#xff0c;這能夠使我們對整個系統有個總體的認識&#xff0c;清楚地知道這個系統有什么能力。這不僅幫助我們學習時快速定位到我們想要的內容&#xff0c;還能避免我們學習過程中在龐大的系統中迷…

java內存圖

java內存圖java文件運行流程程序的內存空間認識虛擬機棧程序的執行流程認識堆java的類與對象的關系java文件運行流程 有這樣的一份 java 文件 在該目錄下的終端運行 javac Hello.java 命令&#xff0c;會生成 Hello.class 文件&#xff0c;內容如下&#xff1a; Hello.java 打…