cpu和内存之间——地址映射
cpu和內(nèi)存之間有三根總線,地址,數(shù)據(jù),和控制總線。
這是在說地址之間的問題。cpu和內(nèi)存之間用地址來查找數(shù)據(jù),但是兩者的地址并不總是一樣的,cpu產(chǎn)生的是邏輯地址,而內(nèi)存的就是物理地址。通常都是不一樣的,所以需要地址映射。
正好是從編程人員的角度看,(不考慮解釋執(zhí)行)程序總是經(jīng)過源程序編譯,連接,運(yùn)行三個(gè)階段。在這個(gè)過程中,指令和數(shù)據(jù)就要調(diào)到內(nèi)存。
地址捆綁的三種形式:
編譯時(shí):編譯時(shí)就生成了絕對(duì)地址。MS-DOS的COM程序就是這樣的。
加載時(shí):編譯時(shí)編譯器生成可重定位代碼,在加載時(shí)捆綁。
執(zhí)行時(shí):執(zhí)行時(shí)才能綁定。
上面說的都是內(nèi)存中的物理地址,對(duì)于編譯時(shí)和加載時(shí)的地址,物理地址和cpu產(chǎn)生的邏輯地址都是一樣的。但是對(duì)于運(yùn)行時(shí)的綁定,邏輯地址(又稱虛擬地址)和物理地址是不一樣的。這時(shí)候就需要有一個(gè)地址映射的問題,這個(gè)問題由MMU(內(nèi)存管理單元)來完成。
這個(gè)實(shí)現(xiàn)其實(shí)就像是分段尋址一樣,有一個(gè)重定位寄存器,其實(shí)就是基址寄存器,cpu生成的都是邏輯地址,也就是說都是個(gè)偏移量,當(dāng)要把數(shù)據(jù)放到內(nèi)存里時(shí),總是要將偏移量加上基址才是真正的物理地址。
用戶處理的也都是邏輯地址,也就是說我們編程時(shí)查看的地址都是邏輯地址。
動(dòng)態(tài)加載:
就是說只有當(dāng)運(yùn)行時(shí),并且程序調(diào)用子程序時(shí),鏈接程序才將子程序調(diào)到內(nèi)存里,否則總是放到磁盤上存儲(chǔ)的。它也是先檢查是否子程序已經(jīng)被加載,沒有的時(shí)候才加載。
動(dòng)態(tài)鏈接:
運(yùn)行時(shí)才進(jìn)行鏈接。否則程序需要的模塊都將被調(diào)入到內(nèi)存,浪費(fèi)空間。
內(nèi)存的空間并不是很大,尤其它必須要裝載操作系統(tǒng)內(nèi)核和用戶進(jìn)程。當(dāng)進(jìn)程分配的空間不夠大時(shí),有兩種策略:覆蓋和交換。
覆蓋就是將內(nèi)存中不再需要的空間覆蓋掉,用來存放需要的數(shù)據(jù)。
交換就是將暫時(shí)不用的空間的數(shù)據(jù)交換出去到輔存,需要時(shí)再調(diào)回來。
但是仍然,對(duì)內(nèi)存空間的合理分配是必要的,不同方法:
連續(xù)分配內(nèi)存。
分頁技術(shù):不需要連續(xù)的內(nèi)存。
分段:用戶思考。
連續(xù)分配內(nèi)存:
將內(nèi)存劃分為固定大小的很多分區(qū),規(guī)定每個(gè)分區(qū)存放一個(gè)進(jìn)程的數(shù)據(jù)。
這是基本思路,它的推廣就是將分區(qū)想象成諸多的孔(hole),總是找合適的孔來存放進(jìn)程。用孔的概念就是來說明它的一定的靈活性,孔是由一些大小不等的孔組成的,但是你看起來還是一個(gè)孔,如果進(jìn)程比較小,就劃出一個(gè)小孔,其他的不用,進(jìn)程大的話就組成一個(gè)大孔。如果進(jìn)程太多就等待,直到可以容納為止。有一點(diǎn)動(dòng)態(tài)分配的意思。
所以它是變長分區(qū)的,而不用一個(gè)固定分區(qū)存放一個(gè)進(jìn)程。
但是本質(zhì)是一樣的,進(jìn)程分配的內(nèi)存空間總是連續(xù)的。這樣有好處,一個(gè)進(jìn)程總是被固定在一定的范圍之內(nèi),這樣有利于進(jìn)程保護(hù):當(dāng)進(jìn)程出錯(cuò)時(shí),它能影響的也只是自己所在的那一塊內(nèi)存,而不會(huì)導(dǎo)致大量的錯(cuò)誤。
但是這樣會(huì)產(chǎn)生碎片。也就是兩個(gè)進(jìn)程之間總有些空間是沒有使用也無法使用的。
所以有了分頁的方法:
它允許進(jìn)程的地址可以是非連續(xù)的。
物理內(nèi)存中還是分為固定大小的片段,叫做幀,對(duì)應(yīng)的,邏輯內(nèi)存(也就是cpu產(chǎn)生的邏輯地址的空間)也分為同樣大的片段,叫做頁,再同樣的,輔存中對(duì)應(yīng)的叫做塊。
由于cpu產(chǎn)生的都是邏輯的地址,于是我們完全可以使它是連續(xù)的,因?yàn)橛脩羰褂玫亩际沁壿嫷刂?#xff0c;所以我們?cè)诓榭吹刂窌r(shí)看到的總是連續(xù)的,我們看到的就是邏輯內(nèi)存。但是在實(shí)際的內(nèi)存中它可以不是這樣的。
想想吧,那么我們編程時(shí)查看的連續(xù)的內(nèi)存,以及我們畫的長方形的內(nèi)存圖,都是邏輯內(nèi)存,物理內(nèi)存我們是沒法知道的,因?yàn)槟鞘遣僮飨到y(tǒng)根據(jù)幀表分配的。幀表是什么呢?幀是物理內(nèi)存中的內(nèi)存片段,它是對(duì)應(yīng)頁的,所以幀表就是記錄那些被使用的頁。比如用1表示,另外不用的用0表示,當(dāng)新的進(jìn)程要求分配內(nèi)存空間時(shí),操作系統(tǒng)就查看幀表就行了,哪些部分是1,忙著呢,哪些部分是0,好可以用!
這些連續(xù)的東西對(duì)應(yīng)到實(shí)際的內(nèi)存的物理空間,如果用連續(xù)內(nèi)存分配的方法,就是一塊地址完全對(duì)應(yīng)。但是用分頁呢,邏輯地址看上去是連續(xù)的,但是它映射到物理地址上就可以有一些處理方法了,這里用的就是一張頁表,很簡單的記錄著cpu上邏輯的地址對(duì)應(yīng)著內(nèi)存中的哪個(gè)物理地址。好像很多事我們都是這樣做的。剩下的就是頁的大小的問題了,想想吧,如果頁太大,比如大到完全容納一個(gè)進(jìn)程,那么就和連續(xù)內(nèi)存分配一樣了,但是如果頁太小,小到內(nèi)存中一個(gè)內(nèi)存單元就是一個(gè)片段,是不是也好像沒問題?但是這樣的話頁表就太可怕了,1G的內(nèi)存需要1G項(xiàng)個(gè)地址么?不能也不需要,所以總要設(shè)定一個(gè)合適的頁大小。
其實(shí)這樣的方法也就是一種重定位,邏輯地址是一個(gè)偏移量,不同的頁就是不同的基址,然后基址加偏移就是物理地址了。
采用分頁的好處呢?它不會(huì)產(chǎn)生外部碎片。但是如果到最后一個(gè)頁內(nèi),它很可能會(huì)產(chǎn)生內(nèi)部碎片的。另外,頁表也需要額外的開銷。
分段:
我們分析程序時(shí),總是考慮它在內(nèi)存中是怎樣的。我們知道全局變量和常量是放在一起的。代碼段是放在棧區(qū)的,動(dòng)態(tài)分配的數(shù)據(jù)是放在堆區(qū)的。但是這些區(qū)真的存在么?即使不存在,我們查看內(nèi)存時(shí),棧區(qū)的地址都是連續(xù)的,堆區(qū)的地址也都是連續(xù)的,好像它就是各個(gè)區(qū)占一部分連續(xù)內(nèi)存的。
但是你從前面方法中能看得出來么?連續(xù)內(nèi)存分配是將整個(gè)進(jìn)程的數(shù)據(jù)都一排溜的放在一起,分頁只是分了內(nèi)存片段,但是對(duì)于具體數(shù)據(jù)它是透明的,它沒有對(duì)各種數(shù)據(jù),指令還是純數(shù)據(jù)做不同的處理。
分段的技術(shù)允許編譯器定義自己的段,我們說的堆區(qū),棧區(qū)就是一種編譯器設(shè)定的。也就是這樣,編譯器才能夠識(shí)別編程代碼。這樣它的很多功能就可以實(shí)現(xiàn)了,比如指令和數(shù)據(jù)放在不同的地方,數(shù)據(jù)是屬于用戶的可以修改,指令卻是屬于操作系統(tǒng)的,它可以被定義為只讀,不能被改變。
分段仍然需要邏輯地址和物理地址的映射。只不過它需要的是段表,段表記錄不同的數(shù)據(jù)位于什么“段”,它當(dāng)然也是用地址標(biāo)記的,但就好象是給內(nèi)存的段取個(gè)名字一樣。地址從幾到幾,代碼段,從幾到幾,堆區(qū)……是不是已經(jīng)很好理解了?
其實(shí)它和分頁是差不多的,分頁也可以這么理解。只不過分頁是固定大小的。而分段卻是動(dòng)態(tài)分配的,并且它有不同的“使命”;
分段也會(huì)有內(nèi)存碎片。
分段和分頁各有優(yōu)缺點(diǎn),段表和頁表都需要額外的開銷。分頁不會(huì)產(chǎn)生外部碎片,但是它隔離了邏輯地址和物理地址,并且毫無章法,完全動(dòng)態(tài)。分段會(huì)產(chǎn)生外部碎片,但是它從我們的思考考慮,給內(nèi)存加了別名?,F(xiàn)在的思路總是取長補(bǔ)短,兩者合并,分段加分頁。
關(guān)鍵詞:
硬件支持:到底采用什么方法來進(jìn)行內(nèi)存管理最大的決定因素是硬件支持。邏輯地址到物理地址的映射都是靠硬件來實(shí)現(xiàn)的。無論是連續(xù)分配還是段表還是頁表,都是一幫子地址寄存器,基址寄存器記錄內(nèi)存片段的開始,而另一個(gè)寄存器保存長度信息等等,編程中的變長數(shù)組(其實(shí)就是堆)就是這么做的。
碎片:
固定大小分配單元(分頁)會(huì)產(chǎn)生內(nèi)部碎片。而不定長的方法(多分區(qū)和分段)會(huì)產(chǎn)生外部碎片。
共享:
分段和分頁時(shí)進(jìn)程總是分為了多個(gè)模塊,如果它們保存的是特定的功能,那么這些模塊可以是共享的。而連續(xù)分配總是一段數(shù)據(jù)都是屬于一個(gè)進(jìn)程。
保護(hù):
仍然是分段和分頁的多個(gè)模塊,如果它們保存的是特定的功能,可以設(shè)置是否只讀,這就是保護(hù)。
總結(jié)
以上是生活随笔為你收集整理的cpu和内存之间——地址映射的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: M2 Pro/Max性能怪兽!苹果新款M
 - 下一篇: 苹果公司 CEO 库克:第一财季收入下滑