標籤:style blog http io os 使用 ar for strong
程式的大部分代碼都可以在必要的時候才載入到記憶體去執行,運行完後可以被直接丟棄或者被其他代碼覆蓋。我們PC上同時跑著很多的應用程式,每個應用程式使用的虛擬位址空間幾乎可以整個線性地址空間(除了部分留給作業系統或者預留它用),可以認為每個應用程式都獨佔了整個虛擬位址空間(字長是32的CPU是4G的虛擬位址空間),但我們的實體記憶體只是1G或者2G。即多個應用程式在同時競爭使用這塊實體記憶體,其必然會導致某個時刻只存在程式的某個片段在執行,也即是所有程式碼和資料分時複用實體記憶體空間—這就是記憶體管理單元(MMU)工作核心作用所在。
處理器系列的晶片(如X86、ARM7以上、MIPS)一般都會有MMU,跟作業系統一塊實現虛擬記憶體管理,MMU也是Linux、Wince等作業系統的硬體要求。而控制器系統的晶片(面向低端控制領域,ARM7,MIPS M系列,80251等)一般都沒有MMU,或者其只有單一的線性映射機制。
本文要談的是控制器領域SoC的記憶體管理單元的硬體設計,其重要的理念同樣是代碼和資料分時複用實體記憶體空間,在保障系統功能和效能的基礎上最大限度地節省實體記憶體的目的。相關的文章包括:SoC軟體架構設計之一:系統記憶體需求評估和節省記憶體的軟體設計技巧。
一、記憶體管理單元(MMU)的工作機制
在闡述控制器領域的記憶體管理之前,還是要先介紹處理器領域的虛擬記憶體管理機制,前者很大程度上是對後者核心機制精髓的借鑒。實現虛擬記憶體管理有幾個模組是協調工作的:CPU、MMU、作業系統、實體記憶體,示(假設該晶片系列沒有cache):
我們根據來分析一下CPU訪問記憶體的過程,假設定址是0x10000008,一頁大小為4K(12位元)。則虛擬位址會分成兩個部分:頁映射部分(20bit,0x10000)+頁內位移(12bit, 0x8)。CPU通過匯流排把地址訊號(0x10000008)送給MMU,MMU會把該地址的頁映射部分(20bit)拿到TLB中匹配。
TLB是什麼東西?Translation Lookaside Buffer,網上有稱為“翻譯後備緩衝器”。這個翻譯都不知道它幹什麼。它的作用就是頁表的緩衝,我喜歡叫它為頁表cache。其結構圖如下:
可以想象,TLB就是索引地址數組,數組的每個元素就是一個索引結構,包含虛擬頁地址和物理頁地址。其在晶片內部表現為寄存器形式,一般寄存器都是32位,實際上TLB中的頁地址也是32位寄存器,只不過索引比較時是比較前20bit,後12bit其實也是有用的,例如可以設定某個bit是表示常駐的,即該索引是永遠有效,不能更換,這種情境一般是為適合一些效能要求特別高的編解碼演算法而設計的。非常駐記憶體的一般在某個時刻(如TLB填滿時訪問一個新的頁地址)就會發生置換。
1) 假如0x10000008的前20bit在TLB中第M個索引中命中,這時就表示該虛擬頁在實體記憶體中已經給它分配好對應的實體記憶體,頁表中也已經做好記錄。至於虛擬位址對應的字碼頁是否從外儲存(flash,card,硬碟)的程式中載入到記憶體中還需要要另外的標記,怎麼標記呢?就是利用上面所講的TLB低12位的某一bit(我們稱為K)來標識,1標識代碼資料已經載入到記憶體,0表示還沒載入到記憶體。假如是1,那就會用M中的物理地址作為高20bit,以頁內位移0x8作為低12bit,形成一個物理地址,送到記憶體去訪問。此時該次訪問就會完成。
2) 假如K是0,那意味著代碼資料尚未載入到記憶體,這時MMU會向中斷管理模組輸出訊號,觸發一個中斷進行核心態,由作業系統負責將對應的字碼頁載入到記憶體。並修改對應頁表項的K位元和TLB對應項的K位元為1.
3) 假如0x10000008的前20bit在TLB所有索引中都沒有命中,則MMU也會向中斷管理模組輸出一個訊號觸發中斷進入核心態,由作業系統將0x10000008右移12位(即除以4K)到頁表中去取得對應的物理頁值,假如物理頁值非0有效,說明代碼已經載入到記憶體了,這時將頁表項的值填入到某一個閒置TLB項中;假如物理頁值為0,說明尚未給這個虛擬頁分配實際的實體記憶體空間,這時會給它分配實際的實體記憶體,並寫好頁表的對應項(這時K是0),最後將這索引項目寫入TLB的其中一條。
2)和3)其實都是在中斷核心態中完成的,為什麼不一塊做了呢?主要是因為一次中斷不應該做太多事情,以加大中斷延時,影響系統效能。當然如果有晶片將兩者做成一個中斷也是可以理解的。我們再來看看頁表的結構。頁表當然也可以按TLB那樣做成索引數組,但是這樣有兩個不好的地方:
1)頁表是要映射所有的虛擬頁面的,其維護在記憶體中也需要不小的空間。頁大小是4K時,那映射全部就是4G/4K=1M條索引,每條索引4*2=8個位元組,就是8M記憶體。
2)假如按TLB那種結構,那匹配索引的過程就是一個for迴圈匹配電路,效率很低,要知道我們做這個都是在中斷態完成的。
所以一般的頁表都是設計成一維數組,即以整個線性虛擬位址空間按頁為單位依次作為數組的下標,即頁表的第一個字(4位元組)就映射虛擬位址空間的最低4K,第二個字映射虛擬位址最低的第二個4K,以此類推,頁表的第N個字就映射虛擬位址空間的第N個4K空間,即(N-1)*4K~4KN的地址空間。這樣頁表的大小就是1M*4=4M位元組,而且匹配索引的時候只是一個位移計算,非常快。
承前啟後,在引出第二部分之前先明確兩個概念:
1. Bank表示代碼分塊的意思,類似於上面提到的頁的概念。
2.不同代碼分時複用記憶體:不同代碼即意味著不同的虛擬位址對應的代碼,(程式連結後的地址都是虛擬位址),記憶體即實體記憶體,即一定大小的不同虛擬位址的代碼在不同的時刻都跑在同一塊一定大小的實體記憶體空間上。每一塊不同的代碼塊即是不同的代碼Bank。
二、控制器領域SoC記憶體管理單元的硬體設計
這裡專指沒有記憶體管理單元的SoC設計,一般為了降低成本,在效能足夠時,如果16位或者24位字長CPU能夠解決問題,一般都不會去選32位字長的CPU,除非是計算效能考慮,或者32位CPU的license更便宜(一般很少見)。只要能夠達到高效地進行記憶體管理,實現實體記憶體分時複用的目的,那都可以稱為是成功或者有效。
有兩種方法可以實現在MMU硬體單元的情況下實現代碼分塊管理:
1)利用工具鏈來實現記憶體分時複用的機制。
2)結合MMU和這個工具鏈實現的分塊處理方法去設計我們新的記憶體管理單元,包括其硬體工作機制和軟體設計和關鍵機制。
由於2)中的內容涉及到在審專利,暫時隱藏,後續再公開,抱歉!
在整合沒有MMU的CPU時,SoC要實現記憶體管理,需要另外設計一個記憶體管理模組,實現MMU的核心功能,即代碼分頁(塊)映射的功能,而且需要簡化設計以達到最高的效率,同時代碼分塊需要直接地體現在連結指令碼上。為了追求效率,編譯連結後的可執行性檔案還會被離線解析組織成一個更簡化的執行檔案,把不需要的段都刪除,並將分塊代碼按邏輯順序放好,以便於作業系統在必要時更快地載入。當然,作業系統的代碼記憶體管理也需要配合記憶體管理硬體電路,並能夠解析重新打包後的執行程式檔案。因此記憶體管理的實現是需要架構師從軟體和硬體上全面考慮,儘可能地在實現核心功能的基礎上簡化電路和設計,涉及的模組包括:硬體機制設計、實體記憶體分配、代碼分塊原則、linker指令碼定義、打包執行檔案、作業系統定製等等。
SoC嵌入式軟體架構設計之二:虛擬記憶體管理原理、MMU硬體設計及代碼分塊管理