9. 自制操作系统: risc-v内存相关介绍
Memory Ordering
-
RVWMO?內存一致性模型
RISC-V使用一種名為“RVWMO”(RISC-V弱內存排序, RISC-V Weak Memory Ordering)的內存模型,旨在為架構師提供靈活性,以構建高性能可伸縮的設計,同時支持易于處理的編程模型.
在RVWMO下,從同一hart上的其他內存指令的角度來看,運行在單個hart上的代碼似乎是按照順序執行的,但來自另一個hart的內存指令可能會觀察到第一個hart上的內存指令以不同的順序執行。因此,多線程代碼可能需要顯式的同步來保證來自不同hart的內存指令之間的順序。基本的RISC-V ISA提供了一個FENCE指令,同時atomic?extension "A"額外定義了load-reserved/store-conditional和atomic read-modify-write指令。
標準ISA擴展還有misaligned aotmics "Zam"和total store ordering"Ztso"?是對RVWMO增加額外的規則。
-
什么是RVWMO內存模型?
RVWMO內存模型是根據全局內存順序定義的,即所有hart產生的內存操作的總順序。通常,一個多線程程序有許多不同的可能執行,每個執行都有自己相應的全局內存順序。全局內存順序定義在由內存指令生成的原語加載和存儲操作上。
-
內存模型原語
內存操作的程序順序反映了生成每個加載和存儲的指令在該 hart 的動態指令流中的邏輯布局順序; 即,順序即一個簡單的有序處理器執行該指令的順序。
內存訪問指令引起內存操作。內存操作既可以是加載操作,也可以是存儲操作,或者兩者同時進行。所有內存操作都是單拷貝原子的:它們永遠不會在部分完成的狀態下被觀察到。
在RV32GC和RV64GC的指令中,每條對齊的內存指令(aligned memory instruction)只產生一個內存操作,有兩個例外。首先,一條不成功的SC指令不會引起任何內存操作。第二,如果XLEN<64, FLD和FSD指令可能各自引起多個內存操作。一個對齊AMO(aligned AMO)產生了一種單一的內存操作,它同時是一個加載操作和一個存儲操作。
一個不對齊的加載或存儲指令可以被分解成一組任意粒度的部分內存操作。XLEN<64的FLD或FSD指令也可以分解為一組任意粒度的部分內存操作。這些指令所產生的內存操作并不是按照程序順序排列的,而是按照之前的指令和之后的指令所產生的內存操作的順序排列的。原子擴展“A”根本不需要執行環境來支持不對齊的原子指令;然而,如果通過“Zam”擴展支持了不對齊的原子,那么LRs、SCs和AMOs可以根據不對齊原子的原子性公理進行分解。
如果在程序順序上,LR指令在SC指令之前,并且中間沒有其他LR或SC指令,則LR指令和SC指令被稱為配對;相應的內存操作也被認為是成對的(除了在SC失敗的情況下,那里沒有存儲操作生成)。
加載和存儲操作也可以攜帶以下集合中的一個或多個ordering字段后綴修飾: “acquire-RCpc”, “acquire-RCsc”, “release-RCpc”,?和“release-RCsc”。一個AMO或LR指令帶有aq后綴,稱為"acquire-RCsc"。一個AMO或SC指令帶有rl后綴,稱為"release-RCsc"。一個AMO、LR或SC指令都帶有aq和rl后綴,稱為acquire-RCsc和release-RCsc
RCpc和RCsc含義
RCsc
全稱:release consistency with sequential consistency?順序松散一致性
RCpc
全稱:release consistency with processor consistency?松散處理器一致性
Machine 物理內存屬性
一個完整系統的物理內存映射包括各種地址范圍,有的對應內存區域,有的對應內存映射的控制寄存器,有的對應地址空間中的空洞。 某些內存區域可能不支持讀取、寫入或執行; 有些可能不支持subword或subblock訪問; 有些可能不支持原子操作; 有些可能不支持緩存一致性,或者可能有不同的內存模型。 類似地,內存映射控制寄存器在其支持的訪問寬度、對原子操作的支持以及讀寫訪問是否具有相關的副作用方面有所不同。 在 RISC-V 系統中,機器物理地址空間的每個區域的這些屬性和功能稱為物理內存屬性 (PMA)。
PMA 是底層硬件的固有屬性,在系統運行期間很少更改。 PMA 不會因執行上下文而異。 某些內存區域的 PMA 在芯片設計時是固定的——例如,對于片上 ROM。 其他的在電路板設計時是固定的,例如,取決于哪些其他芯片連接到片外總線。 片外總線也可能支持在每個電源周期(冷插拔)或系統運行時動態更改(熱插拔)的設備。 某些設備可以在運行時可配置,意味著不同 PMA 的不同用途——例如,片上RAM 可能由一個最終應用程序中的一個內核私有緩存,或在另一個應用程序中作為共享的非緩存內存訪問。
大多數系統將要求在知道物理地址后,在execution pipeline的硬件中至少動態檢查一些 PMA,因為某些操作在所有物理內存地址上都不受支持,并且某些操作需要知道可配置的當前設置 PMA 屬性。 雖然許多其他架構在虛擬內存頁表中指定了一些 PMA 并使用 TLB 來通知pipeline這些屬性,但這種方法將特定于平臺的信息注入到虛擬化層中,并且可能導致系統錯誤,除非在每個頁面中正確初始化屬性 - 每個物理內存區域的表條目。 此外,可用頁面大小對于指定物理內存空間中的屬性可能不是最佳的,從而導致地址空間碎片和昂貴的 TLB 條目的低效使用。
對于 RISC-V,我們將 PMA 的規范和檢查分離到一個單獨的硬件結構中,即 PMA 檢查器。在許多情況下,每個物理地址區域的屬性在系統設計時是已知的,并且可以硬連線到 PMA 檢查器中。在屬性是運行時可配置的情況下,可以提供特定于平臺的內存映射控制寄存器,以適合平臺上每個區域的粒度指定這些屬性(例如,對于可以在可緩存之間靈活劃分的片上 SRAM和不可緩存的用途)。檢查 PMA 是否有任何對物理內存的訪問,包括經過虛擬到物理內存轉換的訪問。為了幫助進行系統調試。我們強烈建議在可能的情況下,RISCV 處理器精確地捕獲未通過 PMA 檢查的物理內存訪問。精確的trap?PMA?非法操作為指令、加載或存儲訪問錯誤異常,與虛擬內存頁面錯誤異常不同。精確的 PMA?trap可能并不總是可能的,例如,當使用這種老架構:probe a legacy bus?。在這種情況下,來自外圍設備的錯誤響應將被報告為不精確的總線錯誤中斷。
PMA 還必須可由軟件讀取,以正確訪問某些設備或正確配置訪問內存的其他硬件組件,例如 DMA 引擎。 由于 PMA 與給定物理平臺的組織緊密相關,因此許多細節本質上是特定于平臺的,軟件可以通過這種方式學習平臺的 PMA 值。 某些設備,尤其是傳統總線,不支持 PMA 的發現,因此如果嘗試不支持的訪問,則會給出錯誤響應或超時。 通常,特定于平臺的機器模式代碼將提取 PMA 并最終使用某些標準表示將此信息呈現給更高層次的低權限軟件。
在平臺支持 PMA 的動態重新配置的情況下,將提供一個接口來通過將請求傳遞給可以正確重新配置平臺的機器模式驅動程序來設置屬性。 例如,在某些內存區域上切換緩存屬性可能涉及特定于平臺的操作,例如緩存刷新,這些操作僅適用于機器模式。
-
主內存與 I/O 與空區域
給定內存地址范圍的最重要特征是它是否擁有常規的主內存或 I/O 設備,或者是空的。 常規主存儲器需要具有許多屬性,具體如下所述,而 I/O 設備可以具有更廣泛的屬性。 不常規主存儲器的內存區域(例如 device scratchpad RAMs)被歸類為 I/O 區域。 空區域也被歸類為 I/O 區域,但具有指定不支持訪問的屬性。
-
支持訪問類型的PMAs
訪問類型指定支持的訪問寬度,從 8 位字節到長多字burst,以及每個訪問寬度是否支持未對齊的訪問。
盡管在 RISC-V hart 上運行的軟件無法直接生成內存bursts,但軟件可能必須對 DMA 引擎進行編程以訪問 I/O 設備,因此可能需要知道支持哪些訪問大小。
主存儲器區域始終支持連接設備所需的所有訪問寬度的讀取和寫入,并且可以指定是否支持指令提取。
有些平臺可能要求所有主存都支持指令獲取。其他平臺可能禁止從一些主存區域獲取指令。
在某些情況下,訪問主存的處理器或設備的設計可能支持其他寬度,但必須能夠使用主存支持的類型。
I/O區域可以指定支持哪些讀、寫組合,或者對哪些數據寬度執行訪問
對于具有基于頁面的虛擬內存的系統,I/O和內存區域可以指定支持哪些硬件頁表讀取和硬件頁表寫入組合。
-
原子的PMAs
原子性PMAs描述了在這個地址區域中支持哪些原子指令。對原子指令的支持分為兩類:LR/SC和AMOs
有些平臺可能要求所有可緩存主存都支持附加處理器所需的所有原子操作。
1.AMO PMA
在AMOs內部,有四個層次的支持:AMONone、AMOSwap、AMOLogical和AMOArithmetic。monone表示不支持AMO操作。AMOSwap表示該地址范圍內只支持AMOSwap指令。AMOLogical表示支持交換指令加上所有的邏輯AMOs (amoand, amoor, amoxor)。AMOArithmetic表示支持所有RISC-V AMOs。對于每個級別的支持,如果底層內存區域支持對該寬度的讀寫,則支持給定寬度的自然對齊的AMOs。只能使用主存和I/O區域。
2. Reservability PMA
Reservability:?可預定性
對于LR/SC,有三個層次的支持 reservability和 eventuality屬性的組合: RsrvNone, RsrvNonEventual,?和RsrvEventual,
RsrvNone表示否支持LR/SC操作(位置不可保留)
RsrvNonEventual表示支持操作(位置是可保留的)
以上兩種無法保證成功。
RsrvEventual表示支持這些操作,并提供最終的成功保證。
LR/SC配合使用,保證對內存的操作是原子的。對于arm64說的內存的獨占訪問。
例子:
使用lr/sc實現內存字M[a0]的比較
0:lr.w a3, (a0) #加載舊值 4:bne a3, a1, 80 #比較舊的值與a1是否相等,不相等則跳到80 8:sc.w a3, a2, (a0) #相等則存入新值a2到M[a0]中 c:bnez a3, 0 #如果存入失敗,則跳轉到0,繼續嘗試80:3.對齊
對于某些地址和訪問寬度,支持對齊的 LR/SC 或對齊的 AMO 的內存區域也可能支持未對齊的 LR/SC 或未對齊的 AMO。 如果對于給定的地址和訪問寬度,未對齊的 LR/SC 或 AMO 生成地址未對齊的異常,則使用該地址和訪問寬度的所有加載、存儲、LR/SC 和 AMO 必須生成地址未對齊的異常。
在標準"A"擴展中是不支持非對齊AMOs或LR/SC的。"Zam"擴展是對非對齊AMOs提供了支持。當前LR/SC非對齊操作還未標準化,因此LR/SC對非對齊內存的操作會產生異常。
在實際實現中,地址未對齊訪問異常可能被access-fault異常替代。如果給定了地址和訪問寬度,所有非對齊LRs/SCs和AMOs訪問將生成access-fault異常。
-
Memory-Ordering PMAs
為了按照FENCE指令和原子指令排序位進行排序,地址空間的區域被劃分為主存或I/O。
一個hart對主存區域的訪問不僅可以被其他hart觀察到,而且可以被其他具有在主存系統中發起請求能力的設備(例如,DMA引擎)觀察到。一致性主內存區域是RVWMO或RVTSO內存模型中的一個。不一致主存區別有 implementation-defined內存模型。
一個hart對I/O區域的訪問不僅可以被其他的hart和總線控制設備觀察到,也可以被目標I/O設備觀察到,并且I/O區域可以被輕松或強順序訪問.其他 hart 和總線主控設備通常以與訪問 RVWMO 內存區域的順序類似的方式觀察到具有寬松排序的 I/O 區域的訪問。 相比之下,對具有強排序的 I/O 區域的訪問通常由其他 hart 和總線主控設備按照程序順序進行觀察。
每個強排序 I/O 區域指定一個編號的排序通道,這是一種可以在不同 I/O 區域之間提供排序保證的機制。 通道 0 僅用于指示點對點強排序,其中只有 hart 對單個關聯 I/O 區域的訪問是強排序的。
通道 1 用于跨所有 I/O 區域提供全局強排序。 hart 對與通道 1 關聯的任何 I/O 區域的任何訪問只能觀察到所有其他 hart 和 I/O 設備按程序順序發生,包括相對于該 hart 對松弛 I/O 區域的訪問或 具有不同通道號的強排序 I/O 區域。 換句話說,對通道 1 中某個區域的任何訪問都相當于在指令之前和之后執行了一個fence io,io 指令。
Other larger channel numbers provide program ordering to accesses by that hart across any regions?with the same channel number.
系統可能支持在每個內存區域上動態配置排序屬性。
-
Coherence and Cacheability PMAs
一致性是為單個物理地址定義的屬性,它表明一個代理對該地址的寫操作最終將對系統中的其他代理可見。不要將一致性與系統的內存一致性模型混淆,該模型定義了在給定整個內存系統的先前讀取和寫入歷史的情況下,內存讀取可以返回的值。在 RISC-V 平臺中,由于軟件復雜性、性能和能量影響,不鼓勵使用硬件不連貫區域。
內存區域的可緩存性不應影響該區域的軟件視圖,除了其他 PMA 中反映的差異,例如主內存與 I/O 分類、內存排序、支持的訪問和原子操作以及一致性。 因此,我們將可緩存性視為僅由機器模式軟件管理的平臺級設置。
在平臺支持內存區域的可配置緩存設置的情況下,平臺特定的機器模式例程將更改設置并在必要時刷新緩存,因此系統僅在緩存設置之間的轉換期間是不連貫的。 這種暫時狀態不應該對較低的特權級別可見
-
Idempotency PMAs
冪等性 PMA 描述了對地址區域的讀取和寫入是否是冪等的。 假定主內存區域是冪等的。 對于 I/O 區域,可以分別指定讀取和寫入的冪等性(例如,讀取是冪等的,但寫入不是)。 如果訪問是非冪等的,即任何讀或寫訪問都可能有副作用,則必須避免推測性或冗余訪問。
出于定義冪等 PMA 的目的,由冗余訪問創建的觀察到的內存順序的變化不被視為副作用。
對于非冪等區域,不得提前或推測性地執行隱式讀取和寫入,但以下情況除外。 當執行非推測性隱式讀取時,允許實現額外讀取包含非推測性隱式讀取地址的自然對齊的 2 次冪區域內的任何字節。 此外,當執行非推測性指令獲取時,允許實現額外讀取下一個自然對齊的相同大小的 2 次方區域內的任何字節(該區域的地址取模 2XLEN)。 這些額外讀取的結果可用于滿足后續的早期或推測性隱式讀取。 這些自然對齊的 2 次冪區域的大小由實現定義,但對于具有基于頁面的虛擬內存的系統,不得超過支持的最小頁面大小。
RISC-V Machine 物理內存保護
?為了支持安全處理和包含故障,希望限制在 hart 上運行的軟件可訪問的物理地址。 可選的物理內存保護 (PMP) 單元提供 per-hart 機器模式控制寄存器,以允許為每個物理內存區域指定物理內存訪問權限(讀、寫、執行)。
PMP 訪問控制設置的粒度是特定于平臺的,但標準 PMP 編碼支持小至四個字節的區域。 某些區域的權限可以hardwired——例如,某些區域可能只在機器模式下可見,但在低權限中不可見。
PMP檢查S或U的所有訪問,包括S和U模式中的指令獲取和數據訪問,以及當設置了mstatus的MPRV和 MPP字段被設置為了S或U時,檢查在M模式下的數據訪問。PMP 檢查也適用于虛擬地址轉換的頁表訪問,其有效特權模式為 S。可選地,PMP 檢查可以額外應用于 M 模式訪問,在這種情況下 PMP 寄存器本身被鎖定,因此即使 M 模式軟件也無法更改它們,直到 hart 被重置。實際上,PMP可以授予S模式和U模式的權限,默認S模式和U模式沒有權限,也可以撤銷m模式的權限,默認m模式有完全權限。
PMP的違規操作會被處理器精確的捕獲
-
物理內存保護CSRs
Physical Memory Protection CSRs
PMP表項由一個8位配置寄存器和一個mxlen位地址寄存器描述。一些 PMP 設置還使用與前面的 PMP 條目關聯的地址寄存器。最多支持64個PMP表項。實現可以實現0個、16個或64個PMP csr;必須首先實現編號最低的PMP CSRs。所有PMP CSR字段為WARL,可以為只讀零。PMP csr只能通過m模式訪問。
PMP 配置寄存器密集地封裝在 CSR 中,以最大限度地減少上下文切換時間。
對于RV32,16個CSRs,pmpcfg0–pmpcfg15,擁有64個PMP條目,pmp0cfg–pmp63cfg。
對RV64,8個偶數的CSRs, pmpcfg0, pmpcfg2,. . . , pmpcfg14,包含了64條PMP條目。但對于奇數編號的CSRs, pmpcfg1, pmpcfg3, . . . , pmpcfg15,是非法的。
PMP地址寄存器CSRs被命名為 pmpaddr0–pmpaddr63。
在RV32中,每個PMP地址寄存器將一個34位物理地址的[33:2]位進行地址編碼
在RV64中,每一個PMP地址寄存器對56位物理地址的[55:2]位進行地址編碼。
并不是所有的物理地址位都可以實現,因此pmpaddr寄存器是WARL。
PMP配置寄存器的布局:
設置R位、W位和X位時,表示PMP表項允許讀、寫和執行指令。當這些位中的一個被清除時,相應的訪問類型被拒絕。R、W、X字段組成一個集合WARL字段,R=0、W=1的組合是被保留的,不符合實際情況。其余兩個字段A和L將在下面的部分中描述。
試圖從沒有執行權限的PMP區域獲取指令會引發指令訪問錯誤異常。在沒有讀取權限的情況下,試圖執行訪問PMP區域內物理地址的load或load-reserved指令會引發load訪問錯誤異常。在沒有寫權限的情況下,嘗試執行訪問PMP區域內物理地址的store、store-conditional或AMO指令會引發store訪問錯誤異常。
Address Matching
一個PMP?entry由一個地址寄存器和一個配置寄存器組成。那么如何知道該PMP?entry控制的物理地址范圍呢?這是有配置寄存器中的A字段和地址寄存器共同決定的。
PMP表項的配置寄存器中的A字段對相關聯的PMP地址寄存器的地址匹配模式進行編碼。該字段的編碼如表3.10所示。
NA4可以看作是NAPOT的特殊情況。
支持另外兩種地址匹配模式:自然對齊的power-of-2區域(NAPOT),包括自然對齊的4字節區域(NA4)的特殊情況;任意范圍的上邊界(TOR)。這些模式支持四字節粒度。
NAPOT范圍利用關聯地址寄存器的低階位來編碼范圍的大小
1.OFF
當A=0時,該PMP表項被禁用,不匹配任何地址。
2. NAPOT
在上圖中,當pmpcfg.A為NAPOT時,從pmpaddr的低位開始尋找連續1的個數。
若pmpaddr值為yyyy...yyy0,即連續1的個數為0,則該PMP?entry鎖控制的地址空間從yyyy....yyyy0開始的8個字節。
若pmpaddr值為yyyy...yy01,即連續1的個數為1,則該PMP?entry鎖控制的地址空間從yyyy....yyy01開始的16個字節。
若pmpaddr值為y...y01...1,? ? ?即連續1的個數為n,? ?則該PMP entry鎖控制的地址空間從y...y00....0開始的2^(n+3)個字節。
這種控制地址范圍的方式叫做字段對齊的2指數地址范圍( naturally aligned power-of-2 regions)
3.TOR
如果選擇了TOR,相關聯的地址寄存器形成地址域的上界,前面的PMP地址寄存器構成地址域的下界,共同構成了一個地址區域。
如果PMP條目 A字段是設置成了TOR,此條目匹配任何地址y, y的范圍pmpaddr[i-1] <= y < pmpaddr[i](不考慮pmpcfg([i-1])。
如果PMP第0個條目的A字段是設置成了TOR,下界則為0,能匹配的地址范圍是y<pmpaddr[0]。
如果pmpaddr[i-1]>=pmpaddr[i],?且pmpcfg[i].A=TOR,?則PMP條目i?沒有匹配的地址。
4.NA4
若pmpaddr值為yyyy...yyyy,此時控制的地址返回從yyyy,,,,yyyy開始的4個字節,pmpcfg.A的值為NA4,即 naturallyaligned four-byte regions
對OFF/NAPOT/NA4的通用方法描述
盡管PMP機制支持最小為4字節的區域,但平臺可以指定更大的PMP區域。通常,PMP粒度為2^(G+2)字節,并且在所有PMP區域中必須相同。
當G>=1,NA4模式是不能被選擇的。
當G>=2且pmpcfg[i].A[1]是設置,例如模式是NAPOT,pmpaddr[i].[G-2:0]讀取全是1。
當G>=1?且pmpcfg[i].A[1]是0,例如模式是OFF或TOR,pmpaddr[i].[G-1:0]讀取全是0。
pmpaddri[G-1:0] 不影響 TOR 地址匹配邏輯。盡管更改 pmpcfg[i].A[1] 會影響從 pmpaddri 讀取的值,但不會影響存儲在該寄存器中的基礎值 — 特別是,當 pmpcfgi.A 從 NAPOT 更改為 TOR/OFF 時,pmpaddri[G-1] 保留其原始值 然后回到 NAPOT。
如果當前 XLEN 大于 MXLEN,則 PMP 地址寄存器會從 MXLEN 到 XLEN 位進行零擴展,以進行地址匹配。
Locking and Privilege Mode
L 位表示 PMP 條目已鎖定,即忽略對配置寄存器和相關地址寄存器的寫入。 鎖定的 PMP 條目保持鎖定狀態,直到 hart 被重置。 如果 PMP 條目 i 被鎖定,則忽略對 pmpicfg 和 pmpaddr[i] 的寫入。 此外,如果 PMP 條目 i 被鎖定并且 pmpicfg.A 設置為 TOR,則對 pmpaddr[i-1] 的寫入將被忽略。
即使A字段設置為OFF,設置L位也會鎖定PMP條目。
我們知道,通常情況下M模式擁有對于所有地址的所有權限。但當L字段為1時,M、S、U模式都必須遵循配置寄存器的權限設置(是否讀、寫、執行權限)。而當L字段為0時,在M模式下匹配到此PMP entry的任何操作都將成功,而S和U模式下需要遵循配置寄存器中的權限設置。
Priority and Matching Logic
(優先級和匹配邏輯)
PMP條目是靜態優先級的。最小編號的PMP條目匹配任何字節確認訪問是成功還是失敗。匹配的PMP條目必須匹配訪問所有字節,否則訪問將失敗,無論L、R、W和X位是什么值。例如,如果PMP條目被配置為匹配四字節訪問0xC-0xF,則建設PMP條目是匹配這些地址的最高優先級條目,則對范圍0x8-0xF的8字節訪問將失敗。如果 PMP 條目匹配訪問的所有字節,則 L、R、W 和 X 位確定訪問是成功還是失敗。 如果 L 位清零且訪問的特權模式為 M,則訪問成功。 否則,如果設置了L位或訪問的特權模式為S或U,則只有設置了訪問類型對應的R、W或X位,則訪問成功。
如果沒有 PMP 條目匹配 M 模式訪問,則訪問成功。 如果沒有PMP表項匹配S-mode或U-mode訪問,但至少實現了一個PMP表項,則訪問失敗。
如果至少實現了一個PMP表項,但是所有PMP表項的A字段都被設置為OFF,那么所有的s模式和u模式內存訪問都會失敗。
訪問失敗會生成指令、加載或存儲訪問錯誤異常。 請注意,單個指令可能會生成多個訪問,這些訪問可能不是相互原子的。 如果一條指令生成的至少一個訪問失敗,則會生成訪問錯誤異常,盡管該指令生成的其他訪問可能會成功并產生可見的副作用。 值得注意的是,引用虛擬內存的指令被分解為多個訪問。
在一些實現中,未對齊的加載、存儲和指令提取也可能被分解為多個訪問,其中一些可能在訪問錯誤異常發生之前成功。 特別是,通過 PMP 檢查的未對齊存儲的一部分可能變得可見,即使另一部分未通過 PMP 檢查。 對于比 XLEN 位寬的浮點存儲(例如,RV32D 中的 FSD 指令),即使存儲地址自然對齊,也會出現相同的行為。
總結
以上是生活随笔為你收集整理的9. 自制操作系统: risc-v内存相关介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 伪元素在父元素中居中_为什么第1号元素是
- 下一篇: uniwide服务器不能进入系统,AMD