LINUX系统编程 LINUX 虚拟内存
生活随笔
收集整理的這篇文章主要介紹了
LINUX系统编程 LINUX 虚拟内存
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
LINUX 虛擬內(nèi)存
以32位操作系統(tǒng)為例子,因為64位系統(tǒng)虛擬地址過大為2^64,32位僅僅為2^32=4G更利于描述,但是原理東西都一樣
這首先要從程序和進程之間的關系開始,我們一般寫好一段C\C++代碼編譯后僅僅為可執(zhí)行文件假設為a.out,我們
運行a.out的時候,這個才叫進程,進程是OS級別抽象的實體(PCB task_struct結構體),為程序運行進行各種檢查和
系統(tǒng)資源分配,一個PCB包含部分信息如下:
(摘至刑文鵬LINUX系統(tǒng)編程講義)
* 進程id。系統(tǒng)中每個進程有唯一的id,在C語言中用pid_t類型表示,其實就是一個非
負整數(shù)。
* 進程的狀態(tài),有運行、掛起、停止、僵尸等狀態(tài)。
* 進程切換時需要保存和恢復的一些CPU寄存器。
* 描述虛擬地址空間的信息。
* 描述控制終端的信息。
* 當前工作目錄(Current Working Directory)。
* umask掩碼。
* 文件描述符表,包含很多指向file結構體的指針。
* 和信號相關的信息。
* 用戶id和組id。
* 控制終端、Session和進程組。
* 進程可以使用的資源上限(Resource Limit)
每個進程分配的內(nèi)存包含很多稱之為段的部分組成并且放到0-3G用戶態(tài)虛擬地址空間中,3-4G為kernel太虛擬地址(注意我們以32位為列),
PCB就存放在我們的kernel態(tài)中。
下面描述0-3G用戶態(tài)虛擬內(nèi)存段
由下向上分別是
1、代碼段,是程序運行的機器代碼,一個程序代碼可以多個程序
? ?同時運行,那么這個代碼段可以同時存在于不同進程的不同
? ?虛擬內(nèi)存地址中,等會用圖說明
2、初始化數(shù)據(jù)段,這個就是C\C++已經(jīng)初始化的全局變量和靜態(tài)變量
? ?我們知道靜態(tài)變量是存在于程序結束,而全局變量(非靜態(tài))的作用
? ?域也是全部代碼塊,那么這些變量需要放到一個非棧空間中
? ?(關于靜態(tài)變量可以查看如下鏈接
??http://blog.itpub.net/7728585/viewspace-2119670/
? ?)
3、未初始化數(shù)據(jù)段,為初始化的全局變量和靜態(tài)變量,未初始化本
? ?段的內(nèi)容初始化為0
4、堆(heap)段,是在運行的時候動態(tài)進程分配的內(nèi)存區(qū)域,比如malloc
下面以一段簡單代碼說明,目的僅僅在于說明上面說的:
(未分配虛擬內(nèi)存地址)
5、棧(stack)段,我們知道棧是一個后進先出的數(shù)據(jù)結構,用于存儲局部
? ?變量,實參和返回值。它由棧幀組成(stack frames),每次新的函數(shù)調(diào)用
? ?都會分配一個新的棧幀比如下面的getv rev都在main函數(shù)棧幀里面。
? ? 而沒有使用到局部變量t 則在add函數(shù)棧幀里面
6、argc,environ 數(shù)組信息,固定大小
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef unsigned int uint;
static uint step=1024;//全局初始化靜態(tài)變量,初始化數(shù)據(jù)段
uint iniv=1; //全局初始化非靜態(tài)變量,可以使用extern訪問,初始化數(shù)據(jù)段
static uint zero;//全局未初始化靜態(tài)變量,未初始化數(shù)據(jù)段
uint add(uint inv) //值傳遞 棧 for add funcation?stack frame
{
? ? ?int t; //?棧?for?add funcation?stack frames ? ? ?return inv*step+zero; //棧 for add funcation?stack frames
}
int main(void)
{
????????uint getv = 10; //棧 for main funcation?stack frame
????????uint rev; //棧 for main funcation?stack frames
????????char* p; //棧 for main funcation?stack frames
????????rev = add(getv);
????????p = calloc(6,1); //堆
????????strcpy(p,"test:");
????????printf("%s%u\n",p,rev);
????????return 0;
} 本來很多圖像自己畫,但是發(fā)現(xiàn)比較麻煩,并且效果可能并不如原圖好,所以直接
摘錄.
關于進程各段組織如下(摘自UNIX/LINUX系統(tǒng)編程手冊)
關于進程用戶態(tài)和內(nèi)核態(tài)的關系如下(摘自刑文鵬LINUX系統(tǒng)編程講義)
為了方便管理LINUX將內(nèi)存分為叫做頁幀的單元(我們熟悉的4K),然后內(nèi)核中就需要保存一份進程虛擬地址到實際地址的映射表,如果訪問的數(shù)據(jù)不再物理內(nèi)存
中就發(fā)生page fault,將磁盤中的數(shù)據(jù)復制到物理內(nèi)存,建立虛擬地址到物理內(nèi)存的映射關系,一個進程訪問數(shù)據(jù)是通過虛擬地址進行訪問,然后通過映射表對應
到實際的物理內(nèi)存。
由于64位系統(tǒng)需要管理的內(nèi)存頁非常巨大在LINUX中使用三級或者四級(內(nèi)核2.6.11以上使用四級)映射表,關于映射表實際實現(xiàn)這里沒有過多討論,因為這個屬于
LINUX內(nèi)核原理的東西,我也沒有能力研究。
(實際是虛擬地址--》線性地址--》物理地址,但是LINUX中虛擬地址和線性地址是相同的。)
映射表直觀圖(摘自UNIX/LINUX系統(tǒng)編程手冊)
四級映射表(摘自pdf內(nèi)存尋址)
最后我們需要牢牢的記住的就是每個進程都有0-4G的虛擬地址空間可供分配,當然沒有分配就是未使用的,進程訪問的是內(nèi)存虛擬地址,虛擬地址空間的數(shù)據(jù)可能并不
在實際內(nèi)存中,當進程訪問到虛擬地址的數(shù)據(jù)并不在內(nèi)存中,那么發(fā)生page fault,將磁盤中的數(shù)據(jù)復制到物理內(nèi)存,建立虛擬地址到物理內(nèi)存的映射關系,如果在實際內(nèi)存不足的情況下啟用swap做為物理內(nèi)存的補充,將部分曾經(jīng)使用過的數(shù)據(jù)而當前沒有使用的數(shù)據(jù)拷貝到SWAP中。而數(shù)據(jù)的過期處理一般為用戶程序自己控制比如LRU鏈表。
(這也是為什么某些數(shù)據(jù)庫比如ORACLE MYSQL,在一臺64G的內(nèi)存的機器上同時跑2個實例都分配64G左右內(nèi)存能夠起來,但是過一段時間可能報內(nèi)存不足的原因)
某些觀點為作者自己觀點如果有誤請指出
參考資料:
1、UNIX/LINUX系統(tǒng)編程手冊
2、LINUX操作系統(tǒng)原理與應用
3、刑文鵬LINUX系統(tǒng)編程講義
4、pdf內(nèi)存尋址
以32位操作系統(tǒng)為例子,因為64位系統(tǒng)虛擬地址過大為2^64,32位僅僅為2^32=4G更利于描述,但是原理東西都一樣
這首先要從程序和進程之間的關系開始,我們一般寫好一段C\C++代碼編譯后僅僅為可執(zhí)行文件假設為a.out,我們
運行a.out的時候,這個才叫進程,進程是OS級別抽象的實體(PCB task_struct結構體),為程序運行進行各種檢查和
系統(tǒng)資源分配,一個PCB包含部分信息如下:
(摘至刑文鵬LINUX系統(tǒng)編程講義)
* 進程id。系統(tǒng)中每個進程有唯一的id,在C語言中用pid_t類型表示,其實就是一個非
負整數(shù)。
* 進程的狀態(tài),有運行、掛起、停止、僵尸等狀態(tài)。
* 進程切換時需要保存和恢復的一些CPU寄存器。
* 描述虛擬地址空間的信息。
* 描述控制終端的信息。
* 當前工作目錄(Current Working Directory)。
* umask掩碼。
* 文件描述符表,包含很多指向file結構體的指針。
* 和信號相關的信息。
* 用戶id和組id。
* 控制終端、Session和進程組。
* 進程可以使用的資源上限(Resource Limit)
每個進程分配的內(nèi)存包含很多稱之為段的部分組成并且放到0-3G用戶態(tài)虛擬地址空間中,3-4G為kernel太虛擬地址(注意我們以32位為列),
PCB就存放在我們的kernel態(tài)中。
下面描述0-3G用戶態(tài)虛擬內(nèi)存段
由下向上分別是
1、代碼段,是程序運行的機器代碼,一個程序代碼可以多個程序
? ?同時運行,那么這個代碼段可以同時存在于不同進程的不同
? ?虛擬內(nèi)存地址中,等會用圖說明
2、初始化數(shù)據(jù)段,這個就是C\C++已經(jīng)初始化的全局變量和靜態(tài)變量
? ?我們知道靜態(tài)變量是存在于程序結束,而全局變量(非靜態(tài))的作用
? ?域也是全部代碼塊,那么這些變量需要放到一個非棧空間中
? ?(關于靜態(tài)變量可以查看如下鏈接
??http://blog.itpub.net/7728585/viewspace-2119670/
? ?)
3、未初始化數(shù)據(jù)段,為初始化的全局變量和靜態(tài)變量,未初始化本
? ?段的內(nèi)容初始化為0
4、堆(heap)段,是在運行的時候動態(tài)進程分配的內(nèi)存區(qū)域,比如malloc
下面以一段簡單代碼說明,目的僅僅在于說明上面說的:
(未分配虛擬內(nèi)存地址)
5、棧(stack)段,我們知道棧是一個后進先出的數(shù)據(jù)結構,用于存儲局部
? ?變量,實參和返回值。它由棧幀組成(stack frames),每次新的函數(shù)調(diào)用
? ?都會分配一個新的棧幀比如下面的getv rev都在main函數(shù)棧幀里面。
? ? 而沒有使用到局部變量t 則在add函數(shù)棧幀里面
6、argc,environ 數(shù)組信息,固定大小
點擊(此處)折疊或打開
摘錄.
關于進程各段組織如下(摘自UNIX/LINUX系統(tǒng)編程手冊)
關于進程用戶態(tài)和內(nèi)核態(tài)的關系如下(摘自刑文鵬LINUX系統(tǒng)編程講義)
為了方便管理LINUX將內(nèi)存分為叫做頁幀的單元(我們熟悉的4K),然后內(nèi)核中就需要保存一份進程虛擬地址到實際地址的映射表,如果訪問的數(shù)據(jù)不再物理內(nèi)存
中就發(fā)生page fault,將磁盤中的數(shù)據(jù)復制到物理內(nèi)存,建立虛擬地址到物理內(nèi)存的映射關系,一個進程訪問數(shù)據(jù)是通過虛擬地址進行訪問,然后通過映射表對應
到實際的物理內(nèi)存。
由于64位系統(tǒng)需要管理的內(nèi)存頁非常巨大在LINUX中使用三級或者四級(內(nèi)核2.6.11以上使用四級)映射表,關于映射表實際實現(xiàn)這里沒有過多討論,因為這個屬于
LINUX內(nèi)核原理的東西,我也沒有能力研究。
(實際是虛擬地址--》線性地址--》物理地址,但是LINUX中虛擬地址和線性地址是相同的。)
映射表直觀圖(摘自UNIX/LINUX系統(tǒng)編程手冊)
四級映射表(摘自pdf內(nèi)存尋址)
最后我們需要牢牢的記住的就是每個進程都有0-4G的虛擬地址空間可供分配,當然沒有分配就是未使用的,進程訪問的是內(nèi)存虛擬地址,虛擬地址空間的數(shù)據(jù)可能并不
在實際內(nèi)存中,當進程訪問到虛擬地址的數(shù)據(jù)并不在內(nèi)存中,那么發(fā)生page fault,將磁盤中的數(shù)據(jù)復制到物理內(nèi)存,建立虛擬地址到物理內(nèi)存的映射關系,如果在實際內(nèi)存不足的情況下啟用swap做為物理內(nèi)存的補充,將部分曾經(jīng)使用過的數(shù)據(jù)而當前沒有使用的數(shù)據(jù)拷貝到SWAP中。而數(shù)據(jù)的過期處理一般為用戶程序自己控制比如LRU鏈表。
(這也是為什么某些數(shù)據(jù)庫比如ORACLE MYSQL,在一臺64G的內(nèi)存的機器上同時跑2個實例都分配64G左右內(nèi)存能夠起來,但是過一段時間可能報內(nèi)存不足的原因)
某些觀點為作者自己觀點如果有誤請指出
參考資料:
1、UNIX/LINUX系統(tǒng)編程手冊
2、LINUX操作系統(tǒng)原理與應用
3、刑文鵬LINUX系統(tǒng)編程講義
4、pdf內(nèi)存尋址
來自 “ ITPUB博客 ” ,鏈接:http://blog.itpub.net/7728585/viewspace-2129073/,如需轉載,請注明出處,否則將追究法律責任。
轉載于:http://blog.itpub.net/7728585/viewspace-2129073/
總結
以上是生活随笔為你收集整理的LINUX系统编程 LINUX 虚拟内存的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 文档服务器地址如何进入,如何登陆服务器地
- 下一篇: ikigai人生四叶草模型:找到你的甜蜜