摘要:這個辦法,在現在計算機的內存管理里面,就叫作內存分頁和分段這樣分配一整段連續(xù)的空間給到程序相比分頁則是把整個物理內存空間切成一段段固定尺寸的大小而對應的程序所需要占用的虛擬內存空間,也會同樣切成一段段固定尺寸的大小。
比爾·蓋茨在上世紀80年代說的“640K ought to be enough for anyone”
也就是“640K內存對哪個人來說都夠用了”
那個年代,微軟開發(fā)的還是DOS操作系統(tǒng),程序員們還在絞盡腦汁,想要用好這極為有限的640K內存
而現在,我手頭的Mac Book Pro已經是16G內存了,上升了一萬倍還不止。
那比爾·蓋茨這句話在當時也是完全的無稽之談么?有沒有哪怕一點點的道理呢?這一講里,我就和你一起來看一看。
1 程序裝載的挑戰(zhàn)在運行這些可執(zhí)行文件的時候,我們其實是通過一個裝載器,解析ELF或者PE格式的可執(zhí)行文件
裝載器會把對應的指令和數據加載到內存里面來,讓CPU去執(zhí)行。
裝載到內存,裝載器需要滿足兩個要求
可執(zhí)行程序加載后占用的內存空間應該是連續(xù)的
執(zhí)行指令的時候,程序計數器是順序地一條一條指令執(zhí)行。這意味著,這一條條指令需要連續(xù)地存儲在一起
需要同時加載很多個程序,并且不能讓程序自己規(guī)定在內存中加載的位置
雖然編譯出來的指令里已經有了對應的各種各樣的內存地址,但是實際加載的時候,我們其實沒有辦法確保,這個程序一定加載在哪一段內存地址上
因為現在的計算機通常會同時運行很多個程序,可能你想要的內存地址已經被其他加載了的程序占用
要滿足這兩個基本的要求,我們很容易想到一個辦法。那就是我們可以在內存里面,找到一段連續(xù)的內存空間,然后分配給裝載的程序,然后把這段連續(xù)的內存空間地址,和整個程序指令里指定的內存地址做一個映射。
指令里用到的內存地址叫作虛擬內存地址(Virtual Memory Address)
實際在內存硬件里面的空間地址,我們叫物理內存地址(Physical Memory Address)
程序里有指令和各種內存地址,我們只需要關心虛擬內存地址就行了
對于任何一個程序來說,它看到的都是同樣的內存地址。我們維護一個虛擬內存到物理內存的映射表,這樣實際程序指令執(zhí)行的時候,會通過虛擬內存地址,找到對應的物理內存地址,然后執(zhí)行。因為是連續(xù)的內存地址空間,所以我們只需要維護映射關系的起始地址和對應的空間大小就可以了。
2 內存分段這種找出一段連續(xù)的物理內存和虛擬內存地址進行映射的方法,我們叫分段(Segmentation)。
這里的段,就是指系統(tǒng)分配出來的那個連續(xù)的內存空間。
分段的辦法很好,解決了程序本身不需要關心具體的物理內存地址的問題,但它也有一些不足之處,第一個就是內存碎片(Memory Fragmentation)
舉個例子
電腦有1GB的內存
先啟動一個圖形渲染程序,占用了512MB的內存
接著啟動一個Chrome瀏覽器,占用了128MB內存
再啟動一個PY程序,占用了256MB內存
這個時候,我們關掉Chrome,于是空閑內存還有1024 - 512 - 256 = 256MB
按理來說,我們有足夠的空間再去裝載一個200MB的程序。但是,這256MB的內存空間不是連續(xù)的,而是被分成了兩段128MB的內存
因此,實際情況是,我們的程序沒辦法加載進來。
當然了,有辦法解決 --- 內存交換(Memory Swapping)
我們可以把Python程序占用的256MB內存寫到硬盤,再從硬盤上讀回來到內存里面
不過讀回來的時候,我們不再把它加載到原來的位置,而是緊緊跟在那已經被占用了的512MB內存后面
這樣,我們就有了連續(xù)的256MB內存空間,就可以去加載一個新的200MB的程序。如果你自己安裝過Linux操作系統(tǒng),你應該遇到過分配一個swap硬盤分區(qū)的問題
這塊分出來的磁盤空間,其實就是專門給Linux操作系統(tǒng)進行內存交換用的。
虛擬內存、分段,再加上內存交換
看起來似乎已經解決了計算機同時裝載運行很多個程序的問題
不過三者的組合仍然會遇到一個性能瓶頸
硬盤的訪問速度要比內存慢很多
而每一次內存交換,我們都需要把一大段連續(xù)的內存數據寫到硬盤上
所以,如果內存交換的時候,交換的是一個很占內存空間的程序,這樣整個機器都會顯得卡頓。
3 內存分頁既然問題出在內存碎片和內存交換的空間太大上,那么解決問題的辦法就是,少出現一些內存碎片。
另外,當需要進行內存交換的時候,讓需要交換寫入或者從磁盤裝載的數據更少一點,這樣就可以解決這個問題。
這個辦法,在現在計算機的內存管理里面,就叫作內存分頁(Paging)
**和分段這樣分配一整段連續(xù)的空間給到程序相比
分頁則是把整個物理內存空間切成一段段固定尺寸的大小**
而對應的程序所需要占用的虛擬內存空間,也會同樣切成一段段固定尺寸的大小。
這樣一個連續(xù)并且尺寸固定的內存空間,我們叫頁(Page)。
從虛擬內存到物理內存的映射,不再是拿整段連續(xù)的內存的物理地址,而是按照一個個頁來的。
頁的尺寸一般遠遠小于整個程序的大小。
在Linux下,我們通常只設置成4KB。你可以通過命令看看你手頭的Linux系統(tǒng)設置的頁的大小。
由于內存空間都是預先劃分好的,也就沒有不能使用的碎片,而只有被釋放出來的很多4KB的頁。
即使內存空間不夠,需要讓現有的、正在運行的其他程序
通過內存交換釋放出一些內存的頁出來,一次性寫入磁盤的也只有少數的一個頁或者幾個頁,不會花太多時間,讓整個機器被內存交換的過程給卡住。
分頁的方式使得加載程序的時候,不再需要一次性把程序加載到物理內存中
可以在進行虛擬內存和物理內存的頁之間的映射后,并不真的把頁加載到物理內存里,而是只在程序運行中,需要用到對應虛擬內存頁里面的指令和數據時,再加載到物理內存里面去。
實際上,我們的操作系統(tǒng),的確是這么做的
當要讀取特定的頁,卻發(fā)現數據并沒有加載到物理內存里的時候,就會觸發(fā)一個來自于CPU的缺頁錯誤(Page Fault)
操作系統(tǒng)會捕捉到這個錯誤,然后將對應的頁,從存放在硬盤上的虛擬內存里讀取出來,加載到物理內存里。這種方式,使得我們可以運行那些遠大于我們實際物理內存的程序。同時,這樣一來,任何程序都不需要一次性加載完所有指令和數據,只需要加載當前需要用到就行了。
通過虛擬內存、內存交換和內存分頁這三個技術的組合,我們最終得到了一個讓程序不需要考慮實際的物理內存地址、大小和當前分配空間的解決方案。
這些技術和方法,對于我們程序的編寫、編譯和鏈接過程都是透明的。這也是我們在計算機的軟硬件開發(fā)中常用的一種方法,就是加入一個間接層。
通過引入虛擬內存、頁映射和內存交換,我們的程序本身,就不再需要考慮對應的真實的內存地址、程序加載、內存管理等問題了。任何一個程序,都只需要把內存當成是一塊完整而連續(xù)的空間來直接使用。
4 總結電腦只要640K內存就夠了嗎?很顯然,現在來看,比爾·蓋茨的這個判斷是不合理的,那為什么他會這么認為呢?因為他也是一個很優(yōu)秀的程序員啊!
在虛擬內存、內存交換和內存分頁這三者結合之下,你會發(fā)現,其實要運行一個程序,“必需”的內存是很少的。CPU只需要執(zhí)行當前的指令,極限情況下,內存也只需要加載一頁就好了。再大的程序,也可以分成一頁。每次,只在需要用到對應的數據和指令的時候,從硬盤上交換到內存里面來就好了。以我們現在4K內存一頁的大小,640K內存也能放下足足160頁呢,也無怪乎在比爾·蓋茨會說出“640K ought to be enough for anyone”這樣的話。
不過呢,硬盤的訪問速度比內存慢很多,所以我們現在的計算機,沒有個幾G的內存都不好意思和人打招呼。
那么,除了程序分頁裝載這種方式之外,我們還有其他優(yōu)化內存使用的方式么?下一講,我們就一起來看看“動態(tài)裝載”,學習一下讓兩個不同的應用程序,共用一個共享程序庫的辦法。
5 推薦閱讀想要更深入地了解代碼裝載的詳細過程,推薦你閱讀《程序員的自我修養(yǎng)——鏈接、裝載和庫》的第1章和第6章。
6 思考在Java這樣使用虛擬機的編程語言里面,我們寫的程序是怎么裝載到內存里面來的呢?它也和我們講的一樣,是通過內存分頁和內存交換的方式加載到內存里面來的么?
jvm已經是上層應用,無需考慮物理分頁,一般更直接是考慮對象本身的空間大小,物理硬件管理統(tǒng)一由承載jvm的操縱系統(tǒng)去解決吧
參考深入淺出計算機組成原理
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76255.html
摘要:鏈接器會掃描所有輸入的目標文件,然后把所有符號表里的信息收集起來,構成一個全局的符號表。這是一本難得的講解程序的鏈接裝載和運行的好書。 showImg(https://image-static.segmentfault.com/396/693/396693929-5d558865c3a7e_articlex); 既然程序最終都被變成了一條條機器碼去執(zhí)行,那為什么同一個程序,在同一臺計算...
摘要:不同的進程,調用同樣的,各自里面指向最終加載的動態(tài)鏈接庫里面的虛擬內存地址是不同的。實際上,在進行程序開發(fā),一直會用到各種各樣的動態(tài)鏈接庫。通過動態(tài)鏈接這個方式,可以說徹底解決了這個問題。參考深入淺出計算機組成原理 showImg(https://image-static.segmentfault.com/734/608/734608610-5d5846d292aa0_articlex...
摘要:計算機組成中的大量原理和設計,都對應著性能這個詞。時間的倒數性能計算機的性能,其實和體力勞動很像,好比是我們要搬東西。對于計算機的性能,我們需要有個標準來衡量。花的時間越少,自然性能就越好。 0 學習路線的知識點概括 showImg(https://segmentfault.com/img/remote/1460000020031616?w=3832&h=2540); 學習計算機組成原...
摘要:在中央處理器的控制部件中,包含的寄存器有指令寄存器和程序計數器。這條指令的第一個操作數,代表累加寄存器在中央處理器中,累加器是一種寄存器,用來儲存計算產生的中間結果。第二個操作數則是進制的的表示。 showImg(https://ask.qcloudimg.com/http-save/1752328/57mlmnq3i5.png); CPU執(zhí)行的也不只是一條指令,一般一個程序包含很多條...
摘要:馮諾依曼體系結構示意圖總結馮諾依曼體系結構確立了我們現在每天使用的計算機硬件的基礎架構。因此,學習計算機組成原理,其實就是學習和拆解馮諾依曼體系結構。 showImg(https://ask.qcloudimg.com/http-save/1752328/g6cdrb45jg.png); 1 計算機的基本硬件組成 早期,DIY一臺計算機,要先有三大件 CPU 內存 主板 1.1 C...
閱讀 2123·2023-04-25 14:56
閱讀 2440·2021-11-16 11:44
閱讀 2696·2021-09-22 15:00
閱讀 1902·2019-08-29 16:55
閱讀 2177·2019-08-29 14:04
閱讀 2305·2019-08-29 11:23
閱讀 3678·2019-08-26 10:46
閱讀 1907·2019-08-22 18:43