通過Linux理解作業系統(四) 記憶體管理(上)

2022-05-13 14:19:46 字數 3332 閱讀 1079

關於記憶體,最直觀的理解可以將其想象成乙個個格仔,每個格仔由乙個位址標記出來並且存了乙個位元組的資料,對於32

位的機器,可以有2^32

個位址,也就是理論上可以存4gb

的資料(實際的機器不一定是4g的物理記憶體)。的確,對於程式設計師而言這樣的理解已經足以滿足我們編寫程式的要求了,而記憶體實際的物理模型也是這個樣子的。但是,對於系統而言,這樣簡單的模型是不夠的,因為正常情況下系統中都會執行著多個程式,如果這些程式都可以直接對任意乙個記憶體位址進行操作,那麼乙個程式就很有可能直接的修改了另外乙個程式儲存在記憶體中的資料,這種情況下會發生什麼,不好說,但肯定會悲劇。所以作業系統必須實現一些機制,來保證各個程序可以和諧友愛地使用這有限的記憶體,同時又要保證記憶體的使用效率,這些就是我本文要講的主要內容了。

1、基本概念

為了解決前面提到的問題,作業系統對物理記憶體做了抽象,得到乙個重要的概念叫做位址空間,指可以用來訪問記憶體的位址集合,也就是0x00000000

到0xffffffff

,大小是4

個g 。每個程序都有自己的位址空間,且在每個程序自己看來這4g

就相當於物理記憶體,它可以使用任意位址去訪問他們,而不需要擔心影響到其他程序。因為這裡的位址並不是實際的實體地址,而是虛擬位址,它需要經過系統轉化成實體地址後再去訪問記憶體,且系統保證了不同的程序中的同乙個虛擬位址會對映到不同的實體地址,

也就不會操作到同一塊記憶體(除非那一塊記憶體是共享的)。又因為通常乙個程式也不會使用到4g

的記憶體,所以4g

的物理記憶體可以同時存放多個程式的資料而不會重疊,即使4

個g都已經放滿了,也可以通過將一部分暫時沒用的資料儲存到磁碟的方式來騰出空間放其它的資料,具體如何操作,我們之後再講,這裡只要知道,我們的程式是通過虛擬位址來訪問記憶體的,而系統保證了每個程序通過位址空間訪問到的都會是自己的資料就可以了。

有了位址空間的概念後,在討論程式如何使用記憶體的時候,我們就可以將物理記憶體的概念拋到一邊了,接下來我們就看看linux

在linux

中,雖然每個程序有4g

位址空間,但是其中只有3g

是屬於它自己的,也就是所謂的使用者空間,剩下的1g

則是所有程序共享的,也就是核心空間,這1g

的核心空間裡儲存了重要的核心資料比如用於分頁查詢的頁表,還有之前提到的程序描述符等,這些內容在系統執行過程中將一直儲存在記憶體當中,且對於執行在使用者模式下的程序是不可見的,只有當程序切換到核心模式後,才能夠對核心空間的資源進行訪問(以及進行系統呼叫的許可權),又因為核心空間是所有程序共享的,所以利用核心空間進行程序間通訊就是一件理所當然的事情了,所有ipc

物件如訊息佇列,共享記憶體和訊號量都存在於核心空間中。

而使用者空間又根據邏輯功能分成了3

個段:text

,data

和 stack

,如下圖所示。

其中,text

段的內容是唯讀的且整個段的大小不會改變,它儲存了程式的執行指令,**於可執行檔案,我們知道程式經過編譯之後會得到乙個可執行檔案,這個可執行檔案裡就儲存了程式執行的機器指令,在執行時,就將這些指令拷貝到text

段裡然後cpu

從這裡讀取指令並執行。

data

段顧名思義是儲存了程式中的資料,包括各種型別的變數,陣列,字串等,它包括兩個部分,乙個是有初始化的資料區,儲存了程式中有初始值的資料,乙個是無初始化的資料區(通常叫做 bss

),儲存了程式中沒有初始值的資料,且bss

區的資料在程式載入時會自動初始化為0

。注意這裡的資料不包括函式內的區域性變數,因為那是在stack

段中的。舉個例子,熟悉c/c++

的人知道如果我們程式中的全域性變數沒有設定初始值的話,會自動初始化為0

,而區域性變數沒有設定初始值的話,則他們的值是不確定的,其原因就在這裡,當全域性量不設初始值時,會儲存在bss

區里,這裡自動為0

,若有初始值,則在有初始化的區,而區域性變數在stack

段則是沒有初始化。跟text

段不同,data

段裡的資料可以被修改,而且data

段的大小也可能在程式執行過程中改變,比如說當呼叫malloc

時,data

段的位址會往上擴充套件,而這些動態分配的記憶體就稱為堆。

stack

段位於使用者空間的最頂部,可以向下增長,它被用來存放進行函式呼叫的棧。當程式執行時,main

函式的棧最先建立,伴隨著傳進來的環境變數和執行引數,並壓入系統棧中(指stack段),當在main

函式中呼叫另乙個函式a

時,系統會先在main

的棧中壓入函式a

的引數和返回位址,並為a

建立乙個新的棧並壓入系統棧中,而當a

返回時,則a

的棧被彈出,這樣就使得當前執行的函式總是在系統棧的頂部(這裡的頂部在上圖中是在下方,因為stack

段是往下增長的),這就是函式呼叫的乙個粗略過程。

2、位址空間的應用

前面已經提到了位址空間的概念,程序只管使用位址空間裡的位址去讀寫資料,而不管實際的資料是放在什麼地方,接下來我們就看看系統利用這點可以幹些什麼。

(1)共享text

段:我們已經知道了text

段是存放程式執行的機器指令的,那麼當多個程序執行同乙個程式的時候,它們的text

段肯定也是一樣的,在這個時候,為了節省物理記憶體,系統是不會把每個程序的text

段內容都放到物理記憶體的,而是只儲存了乙份,然後讓各個程序的位址空間的text

都對映到這一區域,這樣做對於每個程序的執行不會有任何影響,同時又節省了寶貴的物理記憶體。實際上系統還保證了同乙份指令在記憶體中只會存在乙份,乙個實際例子就是動態連線庫的使用。

(2)記憶體對映檔案:因為程序使用位址空間的位址讀寫資料時不用管實際的資料在哪,那就意味著這些資料甚至可以不在記憶體中,記憶體對映檔案就是利用了這一點,通過保留程序位址空間的乙個區域,並將這塊區域對映到磁碟上的乙個檔案,程序就可以像操作記憶體一樣來訪問這個檔案(即像訪問陣列一樣可以使用指標,偏移量等),而不用使用到檔案的io

操作,當然這其中肯定需要作業系統提供相應的機制來去實現邏輯位址到實際檔案存放位置的轉換,但這就不是我們所關心的了。使用記憶體對映檔案還有乙個好處就是,多個程序可以同時對映到同乙個檔案,又因為此時的這乙份檔案在程序看來就是記憶體,也就是說可以將其視為一塊共享記憶體,這意味著每個程序對這塊區域的修改對於其他程序都是實時可見的,當然這裡的效率會比將資料實際放在記憶體時要低,但是卻帶來了另乙個好處就是磁碟空間相對於記憶體來講是無限的,因此可以實現大資料量的資料共享。

通過Linux理解作業系統(一) 概述

通過linux理解作業系統 一 概述 用了那麼多年電腦,作業系統從winxp vista 再到win7 然後是現在用的ubuntu 這麼長的時間裡,一直沒有搞明白這作業系統是個什麼東西,為什麼這麼神奇,只要點一點,按一按,那些一塊一塊的硬體就可以完成我們的工作。直到學了作業系統這門課程,才開始有點朦...

通過VMware安裝Linux作業系統

3.安裝完整名稱只能使用英文本元,不能使用其他的,包括大寫也不行。否則你會卡在那一步過不去的。給你系統起好名字以後你就可以繼續安裝你的系統了,其他的一般設定預設就可以了。安裝路徑不要預設哦,安裝到你提前建好的資料夾中即可。系統大小一般設定為15到20g即可。4.完成上面的操作之後你就可以開始安裝li...

linux作業系統理解 IPC

ipc指程序間通訊方式,注意不是執行緒間,執行緒之間同步只有訊號量和互斥量 1.管道pipe shell的管道就是這個原理 程序管道 popen pclose函式 1.2命名管道fifo,是一種特殊的檔案,在檔案系統中以檔案的形式存在 2.訊號量 備註 學習多程序的同步與互斥,和多線的同步與互斥時,...