CPU和內存之間——地址映射(知識總結)

轉載:http://blog.csdn.net/csulimao/article/details/9275909

CPU和內存之間有三根總線,地址,數據,控制總線。

這是在說地址之間的問題。CPU和內存之間用地址來查找數據,但是兩者的地址並不總是一樣的,CPU產生的是邏輯地址,而內存的就是物理地址。通常都是不一樣的,所以需要地址映射。

正好是從編程人員的角度看,(不考慮解釋執行)程序總是經過源程序編譯,連接,運行三個階段。在這個過程中,指令和數據就要調到內存。

地址捆綁的三種形式:

編譯時:編譯時就生成了絕對地址。MS-DOS的COM程序就是這樣的。

加載時:編譯時編譯器生成可重定位代碼,在加載時捆綁。

執行時:執行時才能綁定。

上面說的都是內存中的物理地址,對於編譯時和加載時的地址,物理地址和CPU產生的邏輯地址都是一樣的。但是對於運行時的綁定,邏輯地址(又稱虛擬地址)和物理地址是不一樣的。這時候就需要有一個地址映射的問題,這個問題由MMU(內存管理單元)來完成。

這個實現其實就像是分段尋址一樣,有一個重定位寄存器,其實就是基址寄存器,cpu生成的都是邏輯地址,也就是說都是個偏移量,當要把數據放到內存裏時,總是要將偏移量加上基址纔是真正的物理地址。

用戶處理的也都是邏輯地址,也就是說我們編程時查看的地址都是邏輯地址。

動態加載:

就是說只有當運行時,並且程序調用子程序時,鏈接程序纔將子程序調到內存裏,否則總是放到磁盤上存儲的。它也是先檢查是否子程序已經被加載,沒有的時候才加載。

動態鏈接:

運行時才進行鏈接。否則程序需要的模塊都將被調入到內存,浪費空間。

內存的空間並不是很大,尤其它必須要裝載操作系統內核和用戶進程。當進程分配的空間不夠大時,有兩種策略:覆蓋和交換。

覆蓋就是將內存中不再需要的空間覆蓋掉,用來存放需要的數據。

交換就是將暫時不用的空間的數據交換出去到輔存,需要時再調回來。

但是仍然,對內存空間的合理分配是必要的,不同方法:

連續分配內存。

分頁技術:不需要連續的內存。

分段:用戶思考。

連續分配內存:

將內存劃分爲固定大小的很多分區,規定每個分區存放一個進程的數據。

這是基本思路,它的推廣就是將分區想象成諸多的孔(hole),總是找合適的孔來存放進程。用孔的概念就是來說明它的一定的靈活性,孔是由一些大小不等的孔組成的,但是你看起來還是一個孔,如果進程比較小,就劃出一個小孔,其他的不用,進程大的話就組成一個大孔。如果進程太多就等待,直到可以容納爲止。有一點動態分配的意思。

所以它是變長分區的,而不用一個固定分區存放一個進程。

但是本質是一樣的,進程分配的內存空間總是連續的。這樣有好處,一個進程總是被固定在一定的範圍之內,這樣有利於進程保護:當進程出錯時,它能影響的也只是自己所在的那一塊內存,而不會導致大量的錯誤。

但是這樣會產生碎片。也就是兩個進程之間總有些空間是沒有使用也無法使用的。

所以有了分頁的方法:

它允許進程的地址可以是非連續的。

物理內存中還是分爲固定大小的片段,叫做幀,對應的,邏輯內存(也就是cpu產生的邏輯地址的空間)也分爲同樣大的片段,叫做頁,再同樣的,輔存中對應的叫做塊。

由於cpu產生的都是邏輯的地址,於是我們完全可以使它是連續的,因爲用戶使用的都是邏輯地址,所以我們在查看地址時看到的總是連續的,我們看到的就是邏輯內存。但是在實際的內存中它可以不是這樣的。

想想吧,那麼我們編程時查看的連續的內存,以及我們畫的長方形的內存圖,都是邏輯內存,物理內存我們是沒法知道的,因爲那是操作系統根據幀表分配的。幀表是什麼呢?幀是物理內存中的內存片段,它是對應頁的,所以幀表就是記錄那些被使用的頁。比如用1表示,另外不用的用0表示,當新的進程要求分配內存空間時,操作系統就查看幀表就行了,哪些部分是1,忙着呢,哪些部分是0,好可以用!

這些連續的東西對應到實際的內存的物理空間,如果用連續內存分配的方法,就是一塊地址完全對應。但是用分頁呢,邏輯地址看上去是連續的,但是它映射到物理地址上就可以有一些處理方法了,這裏用的就是一張頁表,很簡單的記錄着cpu上邏輯的地址對應着內存中的哪個物理地址。好像很多事我們都是這樣做的。剩下的就是頁的大小的問題了,想想吧,如果頁太大,比如大到完全容納一個進程,那麼就和連續內存分配一樣了,但是如果頁太小,小到內存中一個內存單元就是一個片段,是不是也好像沒問題?但是這樣的話頁表就太可怕了,1G的內存需要1G項個地址麼?不能也不需要,所以總要設定一個合適的頁大小。

其實這樣的方法也就是一種重定位,邏輯地址是一個偏移量,不同的頁就是不同的基址,然後基址加偏移就是物理地址了。

採用分頁的好處呢?它不會產生外部碎片。但是如果到最後一個頁內,它很可能會產生內部碎片的。另外,頁表也需要額外的開銷。

分段:

我們分析程序時,總是考慮它在內存中是怎樣的。我們知道全局變量和常量是放在一起的。代碼段是放在棧區的,動態分配的數據是放在堆區的。但是這些區真的存在麼?即使不存在,我們查看內存時,棧區的地址都是連續的,堆區的地址也都是連續的,好像它就是各個區佔一部分連續內存的。

但是你從前面方法中能看得出來麼?連續內存分配是將整個進程的數據都一排溜的放在一起,分頁只是分了內存片段,但是對於具體數據它是透明的,它沒有對各種數據,指令還是純數據做不同的處理。

分段的技術允許編譯器定義自己的段,我們說的堆區,棧區就是一種編譯器設定的。也就是這樣,編譯器才能夠識別編程代碼。這樣它的很多功能就可以實現了,比如指令和數據放在不同的地方,數據是屬於用戶的可以修改,指令卻是屬於操作系統的,它可以被定義爲只讀,不能被改變。

分段仍然需要邏輯地址和物理地址的映射。只不過它需要的是段表,段表記錄不同的數據位於什麼“段”,它當然也是用地址標記的,但就好象是給內存的段取個名字一樣。地址從幾到幾,代碼段,從幾到幾,堆區……是不是已經很好理解了?

其實它和分頁是差不多的,分頁也可以這麼理解。只不過分頁是固定大小的。而分段卻是動態分配的,並且它有不同的“使命”;

分段也會有內存碎片。

分段和分頁各有優缺點,段表和頁表都需要額外的開銷。分頁不會產生外部碎片,但是它隔離了邏輯地址和物理地址,並且毫無章法,完全動態。分段會產生外部碎片,但是它從我們的思考考慮,給內存加了別名。現在的思路總是取長補短,兩者合併,分段加分頁。

關鍵詞:

硬件支持:到底採用什麼方法來進行內存管理最大的決定因素是硬件支持。邏輯地址到物理地址的映射都是靠硬件來實現的。無論是連續分配還是段表還是頁表,都是一幫子地址寄存器,基址寄存器記錄內存片段的開始,而另一個寄存器保存長度信息等等,編程中的變長數組(其實就是堆)就是這麼做的。

碎片:

固定大小分配單元(分頁)會產生內部碎片。而不定長的方法(多分區和分段)會產生外部碎片。

共享:

分段和分頁時進程總是分爲了多個模塊,如果它們保存的是特定的功能,那麼這些模塊可以是共享的。而連續分配總是一段數據都是屬於一個進程。

保護:

仍然是分段和分頁的多個模塊,如果它們保存的是特定的功能,可以設置是否只讀,這就是保護。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章