程式是怎麼裝載到記憶體並被執行的

2021-10-25 20:19:40 字數 3689 閱讀 9457

在後續所有內容之前,我們需要先達成乙個共識,所有的程式都是被裝載進記憶體然後才被使用的。裝載器會把對應的指令和資料載入到記憶體裡面來,讓 cpu 去執行,而程式,包括作業系統就是一堆指令和資料的集合。

下面開始套娃,bios硬體初始化並開始載入主引導扇區(多系統需要選擇啟動哪個系統的原因),將作業系統載入到記憶體;移交載入控制權給作業系統,作業系統開始裝載非作業系統程式到記憶體。因為linux和windows系統的裝載器不同,所以這也是為什麼windows上的一部分程式沒法在linux上跑的原因,如.exe。

裝載器需要滿足兩個要求。

第一,可執行程式載入後占用的記憶體空間應該是連續的,方便定址。

第二,我們需要同時載入很多個程式,並且不能讓程式自己規定在記憶體中載入的位置,避免衝突。

於是我們可以在記憶體裡面,找到一段連續的記憶體空間,然後分配給裝載的程式,然後把這段連續的記憶體空間位址,和整個程式指令裡指定的記憶體位址做乙個對映。我們把指令裡用到的記憶體位址叫作虛擬記憶體位址(virtual memory address),實際在記憶體硬體裡面的空間位址,我們叫物理記憶體位址(physical memory address)。程式裡有指令和各種記憶體位址,我們只需要關心虛擬記憶體位址就行了。

我們維護乙個虛擬記憶體到物理記憶體的對映表,這樣實際程式指令執行的時候,會通過虛擬記憶體位址,找到對應的物理記憶體位址,然後執行。這種方式稱之為分段

但是分段遇到了兩個問題,第乙個是會產生記憶體碎片,還是大碎片

在渲染程式之後我們啟動 chrome 瀏覽器,占用了 128mb 記憶體,再啟動乙個 python 程式,占用了 256mb 記憶體。這個時候,我們關掉 chrome,於是空閒記憶體還有 1024 - 512 - 256 = 256mb。按理來說,我們有足夠的空間再去裝載乙個 200mb 的程式。但是,這 256mb 的記憶體空間不是連續的,而是被分成了兩段 128mb 的記憶體。回想裝載器的兩個原則,我們的程式根本沒辦法載入進來。

那怎麼辦?

我們檢視伺服器的記憶體資訊,你會發現有乙個專門的模組叫做swap,這塊區域就是我們的解決辦法,名為記憶體交換

我們可以把 python 程式占用的那 256mb 記憶體寫到硬碟上,然後再從硬碟上讀回來到記憶體裡面。不過讀回來的時候,我們不再把它載入到原來的位置,而是緊緊跟在那已經被占用了的 512mb 記憶體後面。這樣,我們就有了連續的 256mb 記憶體空間,就可以去載入乙個新的 200mb 的程式。但是乙個8g的記憶體,可能有一半都拿來做交換區了,資源浪費嚴重。

接下來說第二個問題,記憶體**昂貴,一般都不會很大,即使是今天16g、8g仍然是主流,遑論以前了,但是磁碟包括ssd動輒上1t,我如果把程式都載入了,記憶體裝不了啊!

這個問題靠分段已經解決不了了,於是引申出了分頁。因為科學家發現,程式執行在一段時間之內請求的位址是連續的,那我不全載入不就行了,我先載入一小塊或者幾小塊,等我真用到了我再去真實的實體地址那拿一塊,這樣一塊一塊的為頁,也就是分頁思想的由來。

和分段這樣分配一整段連續的空間給到程式相比,分頁是把整個物理記憶體空間切成一段段固定尺寸的大小,且遠比分段小得多,那麼記憶體交換也方便了很多。

在 linux 下,我們通常只設定成 4kb為一頁。由於記憶體空間都是預先劃分好的,也就沒有了不能使用的碎片,而只有被釋放出來的很多 4kb 的頁。即使記憶體空間不夠,需要讓現有的、正在執行的其他程式,通過記憶體交換釋放出一些記憶體的頁出來,一次性寫入磁碟的也只有少數的乙個頁或者幾個頁,不會花太多時間,讓整個機器被記憶體交換的過程給卡住。

分頁的方式使得我們在引導程式的時候,不再需要一次性都把程式載入到物理記憶體中。我們完全可以在進行虛擬記憶體和物理記憶體的頁之間的對映之後,並不真的把頁載入到物理記憶體裡,而是只在程式執行中,需要用到對應虛擬記憶體頁裡面的指令和資料時,再載入到物理記憶體裡面去。

當要讀取特定的頁,卻發現資料並沒有載入到物理記憶體裡的時候,就會觸發乙個來自於 cpu 的缺頁錯誤(page fault)。我們的作業系統會捕捉到這個錯誤,然後將對應的頁,從存放在硬碟上的虛擬記憶體裡讀取出來,載入到物理記憶體裡。這種方式,使得我們可以執行那些遠大於我們實際物理記憶體的程式。

通過虛擬記憶體、記憶體交換和記憶體分頁這三個技術的組合,我們最終得到了乙個讓程式不需要考慮實際的物理記憶體位址、大小和當前分配空間的解決方案。

通過上邊整個的描述你一定對乙個程式是怎麼載入到記憶體並被使用的有了初步了解,那麼你一定也認識到了一點,乙個實體地址可能對映到多個虛擬位址(請牢牢記住這句話),那麼這些虛擬記憶體位址究竟是怎麼轉換成物理記憶體位址的呢?

想要把虛擬記憶體位址,對映到物理記憶體位址,最直觀的辦法,就是來建一張對映表。這個對映表,能夠實現虛擬記憶體裡面的頁,到物理記憶體裡面的頁的一一對映。這個對映表,在計算機裡面,就叫作頁表(page table)。頁表這個位址轉換的辦法,會把乙個記憶體位址分成頁號(directory)和偏移量(offset)兩個部分。這一部分其實可以以cpu的告訴快取作為參考,道理是一樣的。請直接移步cpu快取記憶體原理,這裡只簡單介紹。

把虛擬記憶體位址,切分成頁號和偏移量的組合;

從頁表裡面,查詢出虛擬頁號,對應的物理頁號;

直接拿物理頁號,加上前面的偏移量,就得到了物理記憶體位址。

那麼問題來了,乙個32 位的記憶體位址空間,頁表一共需要記錄 2^20 個到物理頁號的對映關係。這個儲存關係,就好比乙個 2^20 大小的陣列。乙個頁號是完整的 32 位的 4 位元組(byte),這樣乙個頁表就需要 4mb 的空間。怎麼算的?可以移步計算機儲存器 簡單來說就是2^32/4k*4b = 2^20 * 4b = 4m。

每個程序我們都需要維護這麼乙個對映表,那麼請開啟自己的任務管理器看看現在電腦有多少個程序。而我們現在已經普遍都是64位作業系統了。

於是我們又引入了多級頁表的方式,實際的程式程序裡面,虛擬記憶體占用的位址空間,通常是兩段連續的空間。而不是完全散落的隨機的記憶體位址。

我們以乙個 4 級的多級頁表為例,同樣乙個虛擬記憶體位址,偏移量的部分和上面簡單頁表一樣不變,但是原先的頁號部分,我們把它拆成四段,從高到低,分成 4 級到 1 級這樣 4 個頁表索引。

對應的,乙個程序會有乙個 4 級頁表。我們先通過 4 級頁表索引,找到 4 級頁表裡面對應的條目(entry)。這個條目裡存放的是一張 3 級頁表所在的位置,以此類推最後根據偏移量找到對應的實體地址,每一級這個索引長度有多長,取決於你條目的多少,每一級如果都用 5 個位元表示。那麼每一張某 1 級的頁表,只需要 2^5=32 個條目。如果每個條目還是 4 個位元組(32bit),那麼一共需要 128 個位元組。而效果是一樣的,只不過從乘法變成了加法,但是因為多級頁表的存在,也增加了定址時間,原本1次,現在可能需要4次。

as400下程式是怎麼被執行的 一

as400下程式是怎麼被執行的 揭開 400下程式執行神秘的面紗 400的時候有些想法,如有不同的看法,歡迎你提出寶貴意見,我們一起 一下。400下真正可以執行的檔案只有 pgm program srvpgm 不能執行,它只能被 pgm呼叫。這點有點象 windows 平台下,能被系統呼叫執行的只有...

編寫的c程式是怎麼執行的

我是乙個學物理的學生,有個不是很好的習慣,什麼東西不把它的前世今生搞明白了就特別難受,接觸計算機語言的時候,看著自己編的 c語言 執行了,但是總是有種蒙在鼓裡的感覺,計算機這個東東是怎麼執行的我的指令,於是我就開始搜尋一些資料,從微機原理開始,一步步,雖然還有些地方不懂,但是大體可以搞明白了,我向來...

程式是如何被計算機所執行的?

一 電腦程式 要明白程式是如何被計算機所執行的,首先要明白什麼是程式?先看看一下幾個問題 二 程式的執行過程 當我們輸入以下程式,編譯執行,計算機從螢幕輸出hello,world 整個過程計算機都怎麼運作的呢?include void main 計算機內部儲存的是0和1,計算機通過位資訊以及上下文來...