1. 程式人生 > >堆、棧、重對映及記憶體分配理解

堆、棧、重對映及記憶體分配理解

所謂的理解硬體就是說,理解這個硬體是怎麼組織這麼多資源的,這些資源又是怎麼由cpu、由程式設計進行控制的。

比如說,s3c2410中有AD轉換器,有GPIO(通用IO口),還有nandflash控制器,這些東西都有一些暫存器來控制,這些暫存器都有一個地址,那麼這些地址是什麼意思?(也即是在記憶體中的位置,每個處理器都有其定址的範圍,我們把這個範圍從0開始編號,一般都是按位元組編址的,暫存器的地址就是在這個記憶體中的位置)

又怎麼通過暫存器來控制這些外圍裝置的運轉?(這個就是硬體的設計的功能了,例如手機開關按鍵按下就可控制手機的亮滅。向相應的暫存器中寫入對應的資料就可實現相應的功能

還有,norflash

內部的每一個單元在這個晶片的記憶體中都有一個相應的地址單元,那麼這些地址與剛剛說的暫存器地址又有什麼關係?(可以說是一樣的,不同之處是放入上面暫存器內的資料是控制相應外設的功能的,而norflash內的是用於儲存資料的)

再有,使用ADS進對ARM9行程式設計時都需要使用到一個初始化的彙編檔案,這個檔案究竟有什麼用?(指的是啟動程式碼吧

他裡面的程式碼是什麼意思?(為能順利的執行我們用高階語言寫的程式做準備的,例如硬體初始化,堆疊的分配等

不要這個可以嗎?(如果你的應用程式是使用匯編編寫的就不需要這些

諸如此類都是對硬體的理解,理解了這些東西就對硬體有很深的理解了,這對以後更深一步的學習將有很大的幫助。

每個設計的確都有它的現實考慮,程式語言是很實在的東西,往往外貌冷冰冰但其為什麼是這樣有充足原因。

1.內建陣列,為什麼不設定下標檢測?如果檢測下標,定然就會在每次訪問下標時,做是否越界的檢驗,這就帶來了執行時開銷。如果你的演算法非常好,定然不需要檢測下標,則語言假定一定要在每次訪問下標時都判斷,就會影響效率並失去選擇的機會。如果設定N個選項,可以用來關閉或開啟是否檢測下標,那不應該是一種語言應該乾的,各有各的側重點。

2.C語言傳陣列引數為什麼預設是轉換成指標型別?以C語言產生那個年代的硬體條件,複製陣列很奢侈,尤其函式被呼叫往往很頻繁,演算法要儘量往不復制的情況下設計,如果實在必要,非要複製,你也可以手動memcpy嘛!總之它不是預設項。C++給了使用者另一種選項,即通過加上引用,而使得能夠真正傳整個陣列。


3.for語句為什麼有的靈活有的嚴格?像在Ada中的語法,便是禁止迴圈變數被改變,且不能設定步長值,要想達到這兩個目的,便只能用其他變數再過渡,這樣做是為了高度的安全。反之,C語言的for則非常靈活,也沒有Ada那麼多的限制,但這種靈活並不能保證使用者用其寫出錯誤邏輯的程式碼;VB的自由度則介於二者之間。不能因為這些語言的設計不同,而指責其中某一種語言為何不對某一語法特性做必要的限制,它真的必要嗎?個案好說,但綜合全域性,很難評估。

儲存器對映與重對映

儲存器對映是指把晶片中或晶片外的FLASH,RAM,外設等進行統一編址。即用地址來表示物件。這個地址絕大多數是由廠家規定好的,使用者只能用而不能改。使用者只能在掛外部RAM或FLASH的情況下可進行自定義。

儲存器對映就是對各種儲存器的大小和地址分佈的規劃。儲存器重對映就是為了快速響應中斷或者快速完成某個任務,將同一地址段對映到不同速度的兩個儲存塊,對低速儲存塊的訪問將被重對映為對高速儲存塊的訪問。

為什麼需要儲存器重對映

目前很多嵌入式系統中的Flash分為Code FlashData FlashCode Flash存放可以執行的程式程式碼,Data Flash存放應用資料。Code Flash通常被對映到0地址,系統上電覆位後就從0地址開始執行。通常中斷向量表就位於0地址的起始位置,系統上電覆位後執行的第一條指令就是復位中斷的跳轉指令。

由於中斷向量表位於CodeFlash中,Code Flash的讀取速度比RAM慢,為了快速響應中斷,就將RAM重對映到0地址,然後將中斷向量表複製到RAM的起始位置。再發生中斷時,訪問Code Flash中的中斷向量表,將被重對映為訪問RAM中的中斷向量表。

Boot Block是晶片設計廠商在LPC2000系列ARM內部固化的一段程式碼,使用者無法對其進行修改或者刪除。為了增加使用者程式碼的可移植性,所以最好把Boot Block的程式碼固定的某個地址上於是晶片廠家將Boot Block的地址重對映到片記憶體儲器空間的最高階,即接近2Gb的地方,這樣無論片記憶體儲器的大小如何,都不會影響Boot Block的地址。

棧和堆的區別

棧是由編譯器在需要時分配的,不需要時自動清除的變數儲存區。裡面的變數通常是區域性變數、函式引數等。堆是由malloc()函式(C++語言為new運算子)分配的記憶體塊,記憶體釋放由程式設計師手動控制,在C語言為free函式完成(C++中為delete)。棧和堆的主要區別有以下幾點:

(1)管理方式不同。

棧編譯器自動管理,無需程式設計師手工控制;而堆空間的申請釋放工作由程式設計師控制,容易產生記憶體洩漏。(設計多工作業系統時還是需要自己為任務設計棧)

(2)空間大小不同。

棧是向低地址擴充套件的資料結構,是一塊連續的記憶體區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,當申請的空間超過棧的剩餘空間時,將提示溢位。因此,使用者能從棧獲得的空間較小。

堆是向高地址擴充套件的資料結構,是不連續的記憶體區域。因為系統是用連結串列來儲存空閒記憶體地址的,且連結串列的遍歷方向是由低地址向高地址。由此可見,堆獲得的空間較靈活,也較大。棧中元素都是一一對應的,不會存在一個記憶體塊從棧中間彈出的情況。

(3)是否產生碎片。

對於堆來講,頻繁的malloc/free(new/delete)勢必會造成記憶體空間的不連續,從而造成大量的碎片,使程式效率降低(雖然程式在退出後作業系統會對記憶體進行回收管理)。對於棧來講,則不會存在這個問題。

(4)增長方向不同。

堆的增長方向是向上的,即向著記憶體地址增加的方向;棧的增長方向是向下的,即向著記憶體地址減小的方向。(不同的處理器可能不同)

(5)分配方式不同。

堆都是程式中由malloc()函式動態申請分配並由free()函式釋放的;棧的分配和釋放是由編譯器完成的,棧的動態分配由alloca()函式完成,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器進行申請和釋放的,無需手工實現。

(6)分配效率不同。

棧是機器系統提供的資料結構,計算機會在底層對棧提供支援:分配專門的暫存器存放棧的地址,壓棧出棧都有專門的指令執行。堆則是C函式庫提供的,它的機制很複雜,例如為了分配一塊記憶體,庫函式會按照一定的演算法(具體的演算法可以參考資料結構/作業系統)在堆記憶體中搜索可用的足夠大的空間,如果沒有足夠大的空間(可能是由於記憶體碎片太多),就有需要作業系統來重新整理記憶體空間,這樣就有機會分到足夠大小的記憶體,然後返回。顯然,堆的效率比棧要低得多。

記憶體分配方式

記憶體分配方式有三種:

[1]從靜態儲存區域分配。該記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個執行期間都存在。例如全域性變數,static變數。

[2]在棧上建立。在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束時這些儲存單元自動被釋放。棧記憶體分配運算內置於處理器的指令集中,效率很高,但是分配的記憶體容量有限。

[3]從堆上分配,亦稱動態記憶體分配。程式在執行的時候用mallocnew申請任意多少的記憶體,程式設計師自己負責在何時用freedelete釋放記憶體。動態記憶體的生存期由程式設計師決定,使用非常靈活,但如果在堆上分配了空間,就有責任回收它,否則執行的程式會出現記憶體洩漏,頻繁地分配和釋放不同大小的堆空間將會產生堆內碎塊。

一個由C/C++編譯的程式佔用的記憶體分為以下幾個部分

1、棧區(stack由編譯器自動分配釋放,存放為執行函式而分配的區域性變數、函式引數、返回資料、返回地址等。

2、堆區(heap一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由OS回收。分配方式類似於連結串列。

3、全域性區(靜態區)(static)存放全域性變數、靜態資料、常量。程式結束後由系統釋放

4、文字常量區常量字串就是放在這裡的。程式結束後由系統釋放。

5、程式程式碼區存放函式體(類成員函式和全域性函式)的二進位制程式碼。