99精品伊人亚洲|最近国产中文炮友|九草在线视频支援|AV网站大全最新|美女黄片免费观看|国产精品资源视频|精彩无码视频一区|91大神在线后入|伊人终合在线播放|久草综合久久中文

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

深入淺出Linux的進(jìn)程地址空間

如意 ? 來(lái)源:CSDN ? 作者:程姚根 ? 2020-06-20 09:57 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

我們知道,在32位機(jī)器上linux操作系統(tǒng)中的進(jìn)程的地址空間大小是4G,其中0-3G是用戶(hù)空間,3G-4G是內(nèi)核空間。其實(shí),這個(gè)4G的地址空間是不存在的,也就是我們所說(shuō)的虛擬內(nèi)存空間。

那虛擬內(nèi)存空間是什么呢,它與實(shí)際物理內(nèi)存空間又是怎樣對(duì)應(yīng)的呢,為什么有了虛擬內(nèi)存技術(shù),我們就能運(yùn)行比實(shí)際物理內(nèi)存大的應(yīng)用程序,它是怎么做到的呢?呵呵,這一切的一切都是個(gè)迷呀,下面我們就一步一步解開(kāi)心中的謎團(tuán)吧!

我們來(lái)看看,當(dāng)我們寫(xiě)好一個(gè)應(yīng)用程序,編譯后它都有什么東東?

例如:

用命令size a.out會(huì)得到:

其中text是放的是代碼,data放的是初始化過(guò)的全局變量或靜態(tài)變量,bss放的是未初始化的全局變量或靜態(tài)變量。

由于歷史原因,C程序一直由下列幾部分組成:

A、正文段。這是由cpu執(zhí)行的機(jī)器指令部分。通常,正文段是可共享的,所以即使是經(jīng)常執(zhí)行的程序(如文本編輯程序、C編譯程序、shell等)在存儲(chǔ)器中也只需要有一個(gè)副本,另外,正文段常常是只讀的,以防止程序由于意外事故而修改器自身的指令。

B、初始化數(shù)據(jù)段。通常將此段稱(chēng)為數(shù)據(jù)段,它包含了程序中需賦初值的變量。例如,C程序中任何函數(shù)之外的說(shuō)明:

int maxcount = 99;(全局變量)

C、非初始化數(shù)據(jù)段。通常將此段稱(chēng)為bss段,這一名稱(chēng)來(lái)源于早期匯編程序的一個(gè)操作,意思是“block started by symbol”,在程序開(kāi)始執(zhí)行之前,內(nèi)核將此段初始化為0。函數(shù)外的說(shuō)明:

long sum[1000];

使此變量存放在非初始化數(shù)據(jù)段中。

D、棧。自動(dòng)變量以及每次函數(shù)調(diào)用時(shí)所需保存的信息都存放在此段中。每次函數(shù)調(diào)用時(shí),其返回地址、以及調(diào)用者的環(huán)境信息(例如某些機(jī)器寄存器)都存放在棧中。然后,新被調(diào)用的函數(shù)在棧上為其自動(dòng)和臨時(shí)變量分配存儲(chǔ)空間。通過(guò)以這種方式使用棧,C函數(shù)可以遞歸調(diào)用。

E、堆。通常在堆中進(jìn)行動(dòng)態(tài)存儲(chǔ)分配。由于歷史上形成的慣例,堆位于非初始化數(shù)據(jù)段頂和棧底之間。

從上圖我們看到??臻g是下增長(zhǎng)的,堆空間是從下增長(zhǎng)的,他們會(huì)會(huì)碰頭呀?一般不會(huì),因?yàn)樗麄冎g間隔很大,如:

#include 《stdio.h》

#include 《stdlib.h》

int bss_var;

int data_var0 = 1;

int main()

{

printf(“Test location:\n”);

printf(“\tAddress of main(Code Segment):%p\n”,main);

printf(“_____________________________________\n”);

int stack_var0 = 2;

printf(“Stack location:\n”);

printf(“\tInitial end of stack:%p\n”,&stack_var0);

int stack_var1 = 3;

printf(“\tNew end of stack:%p\n”,&stack_var1);

printf(“_____________________________________\n”);

printf(“Data location:\n”);

printf(“\tAddress of data_var(Data Segment):%p\n”,&data_var0);

static int data_var1 = 4;

printf(“\tNew end of data_var(Data Segment):%p\n”,&data_var1);

printf(“_____________________________________\n”);

printf(“BSS location:\n”);

printf(“\tAddress of bss_var:%p\n”,&bss_var);

printf(“_____________________________________\n”);

printf(“Heap location:\n”);

char *p = (char *)malloc(10);

printf(“\tAddress of head_var:%p\n”,p);

return 0;

}

運(yùn)行結(jié)果如下:

呵呵,這里我們看到地址了,這個(gè)地址是虛擬地址,這些地址時(shí)怎么來(lái)的呢?其實(shí)在我們編譯的時(shí)候,這些地址就已經(jīng)確定了,如下圖中紅線。

也就是說(shuō),我們不論我們運(yùn)行a.out程序多少次這些地址都是一樣的。我們知道,linux操作系統(tǒng)每個(gè)進(jìn)程的地址空間都是獨(dú)立的,其實(shí)這里的獨(dú)立說(shuō)得是物理空間上得獨(dú)立。那相同的虛擬地址,不同的物理地址,他們之間是怎樣聯(lián)系起來(lái)的呢?我們繼續(xù)探究。。.。

在linux操作系統(tǒng)中,每個(gè)進(jìn)程都通過(guò)一個(gè)task_struct的結(jié)構(gòu)體描敘,每個(gè)進(jìn)程的地址空間都通過(guò)一個(gè)mm_struct描敘,c語(yǔ)言中的每個(gè)段空間都通過(guò)vm_area_struct表示,他們關(guān)系如下 :

當(dāng)運(yùn)行一個(gè)程序時(shí),操作系統(tǒng)需要?jiǎng)?chuàng)建一個(gè)進(jìn)程,這個(gè)進(jìn)程和程序之間都干了些什么呢?

當(dāng)一個(gè)程序被執(zhí)行時(shí),該程序的內(nèi)容必須被放到進(jìn)程的虛擬地址空間,對(duì)于可執(zhí)行程序的共享庫(kù)也是如此。可執(zhí)行程序并非真正讀到物理內(nèi)存中,而只是鏈接到進(jìn)程的虛擬內(nèi)存中。

當(dāng)一個(gè)可執(zhí)行程序映射到進(jìn)程虛擬地址空間時(shí),一組vm_area_struct數(shù)據(jù)結(jié)構(gòu)將被產(chǎn)生。每個(gè)vm_area_struct數(shù)據(jù)結(jié)構(gòu)表示可執(zhí)行印象的一部分;是可執(zhí)行代碼,或是初始化的數(shù)據(jù),以及未初始化的數(shù)據(jù)等。

linux操作系統(tǒng)是通過(guò)sys_exec對(duì)可執(zhí)行文件進(jìn)行映射以及讀取的,有如下幾步:

1、創(chuàng)建一組vm_area_struct;

2、圈定一個(gè)虛擬用戶(hù)空間,將其起始結(jié)束地址(elf段中已設(shè)置好)保存到vm_start和vm_end中;

3、將磁盤(pán)file句柄保存在vm_file中;

4、將對(duì)應(yīng)段在磁盤(pán)file中的偏移值(elf段中已設(shè)置好)保存在vm_pgoff中;

5、將操作該磁盤(pán)file的磁盤(pán)操作函數(shù)保存在vm_ops中;

注意:這里沒(méi)有對(duì)應(yīng) 的頁(yè)目錄表項(xiàng)創(chuàng)建頁(yè)表,更不存在設(shè)置頁(yè)表項(xiàng)了。

假設(shè)現(xiàn)在程序中有一條指令需要讀取上面vm_start--vm_end之間的某內(nèi)容

例如:mov [0x08000011],%eax,那么將會(huì)執(zhí)行如下序列:

1、cpu依據(jù)CR3(current-》pgd)找到0x08000011地址對(duì)應(yīng)的pgd[i],由于該pgd[i]內(nèi)容保持為初始化狀態(tài)即為0,導(dǎo)致cpu異常。

2、.do_page_fault被調(diào)用,在該函數(shù)中,為pgd[i]在內(nèi)存中分配一個(gè)頁(yè)表,并讓該表項(xiàng)指向它,如下圖所示:

注意:這里i為0x08000011高10位,j為其中間10位,此時(shí)pt表項(xiàng)全部為0(pte[j]也為0);

3、為pte[j]分配一個(gè)真正的物理內(nèi)存頁(yè)面,依據(jù)vm_area_struct中的vm_file、vm_pgoff和vm_ops,調(diào)用filemap_nopage將磁盤(pán)file中vm_pgoff偏移處的內(nèi)容讀入到該物理頁(yè)面中,如下圖所示:

①分配物理內(nèi)存頁(yè)面;

②從磁盤(pán)文件中將內(nèi)容讀取到物理內(nèi)存頁(yè)面中

從上面我們可以知道,在進(jìn)程創(chuàng)建的過(guò)程中,程序內(nèi)容被映射到進(jìn)程的虛擬內(nèi)存空間,為了讓一個(gè)很大的程序在有限的物理內(nèi)存空間運(yùn)行,我們可以把這個(gè)程序的開(kāi)始部分先加載到物理內(nèi)存空間運(yùn)行,因?yàn)椴僮飨到y(tǒng)處理的是進(jìn)程的虛擬地址,如果在進(jìn)行虛擬到物理地址的轉(zhuǎn)換工程中,發(fā)現(xiàn)物理地址不存在時(shí),這個(gè)時(shí)候就會(huì)發(fā)生缺頁(yè)異常(nopage),接著操作系統(tǒng)就會(huì)把磁盤(pán)上還沒(méi)有加載到內(nèi)存中的數(shù)據(jù)加載到物理內(nèi)存中,對(duì)應(yīng)的進(jìn)程頁(yè)表進(jìn)行更新。也許你會(huì)問(wèn),如果此時(shí)物理內(nèi)存滿(mǎn)了,操作系統(tǒng)將如何處理?

下面我們看看linux操作系統(tǒng)是如何處理的:

如果一個(gè)進(jìn)程想將一個(gè)虛擬頁(yè)裝入物理內(nèi)存,而又沒(méi)有可使用的空閑物理頁(yè),操作系統(tǒng)就必須淘汰物理內(nèi)存中的其他頁(yè)來(lái)為此頁(yè)騰出空間。

在linux操作系統(tǒng)中,物理頁(yè)的描敘如下:

struct mem_map

{

1、本頁(yè)使用計(jì)數(shù),當(dāng)該頁(yè)被許多進(jìn)程共享時(shí)計(jì)數(shù)將大于1

2、age描敘本頁(yè)的年齡,用來(lái)判斷該頁(yè)是否為淘汰或交換的好候選

3、map_nr描敘物理頁(yè)的頁(yè)幀號(hào)

}

如果從物理內(nèi)存中被淘汰的頁(yè)來(lái)自于一個(gè)映像或數(shù)據(jù)文件,并且還沒(méi)有被寫(xiě)過(guò),則該頁(yè)不必保存,它可以丟掉。如果有進(jìn)程在需要該頁(yè)時(shí)就可以把它從映像或數(shù)據(jù)文件中取回內(nèi)存。

然而,如果該頁(yè)被修改過(guò),操作系統(tǒng)必須保留該頁(yè)的內(nèi)容以便晚些時(shí)候在被訪問(wèn)。這種頁(yè)稱(chēng)為“臟(dirty)頁(yè)”,當(dāng)它被從內(nèi)存中刪除時(shí),將被保存在一個(gè)稱(chēng)為交換文件的特殊文件中。

相對(duì)于處理器和物理內(nèi)存的速度,訪問(wèn)交換文件要很長(zhǎng)時(shí)間,操作系統(tǒng)必須在將頁(yè)寫(xiě)到磁盤(pán)以及再次使用時(shí)取回內(nèi)存的問(wèn)題上花費(fèi)心機(jī)。

如果用來(lái)決定哪一頁(yè)被淘汰或交換的算法不夠高效的話(huà),就可能出現(xiàn)稱(chēng)為“抖動(dòng)”的情況。在這種情況下,頁(yè)面總是被寫(xiě)到磁盤(pán)又讀回來(lái),操作系統(tǒng)忙于此而不能進(jìn)行真正的工作。

linux使用“最近最少使用(Least Recently Used ,LRU)”頁(yè)面調(diào)度技巧來(lái)公平地選擇哪個(gè)頁(yè)可以從系統(tǒng)中刪除。這種設(shè)計(jì)系統(tǒng)中每個(gè)頁(yè)都有一個(gè)“年齡”,年齡隨頁(yè)面被訪問(wèn)而改變。頁(yè)面被訪問(wèn)越多它越年輕;被訪問(wèn)越少越老。年老的頁(yè)是用于交換的最佳候選頁(yè)。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11509

    瀏覽量

    213724
  • ip地址
    +關(guān)注

    關(guān)注

    0

    文章

    306

    瀏覽量

    18397
  • 進(jìn)程
    +關(guān)注

    關(guān)注

    0

    文章

    207

    瀏覽量

    14277
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    深入淺出AVR

    深入淺出AVR,一本書(shū)。
    發(fā)表于 07-15 12:02

    深入淺出玩轉(zhuǎn)FPGA

    深入淺出玩轉(zhuǎn)FPGA
    發(fā)表于 07-21 09:21

    深入淺出Linux_設(shè)備驅(qū)動(dòng)編程

    深入淺出Linux_設(shè)備驅(qū)動(dòng)編程
    發(fā)表于 08-16 15:57

    深入淺出ARM7

    深入淺出ARM7
    發(fā)表于 08-18 10:12

    HDMI技術(shù)深入淺出

    HDMI技術(shù)深入淺出
    發(fā)表于 08-19 10:52

    深入淺出Android

    深入淺出Android
    發(fā)表于 08-20 10:14

    深入淺出Linux_設(shè)備驅(qū)動(dòng)編程

    深入淺出Linux_設(shè)備驅(qū)動(dòng)編程
    發(fā)表于 08-20 14:58

    深入淺出Android

    深入淺出Android
    發(fā)表于 04-26 10:48

    深入淺出安防視頻監(jiān)控系統(tǒng)

    深入淺出安防視頻監(jiān)控系統(tǒng)深入淺出安防視頻監(jiān)控系統(tǒng)
    發(fā)表于 05-22 19:28

    深入淺出AVR

    深入淺出AVR
    發(fā)表于 08-23 10:10

    深入淺出數(shù)據(jù)分析

    深入淺出數(shù)據(jù)分析,有需要的朋友下來(lái)看看。
    發(fā)表于 01-15 14:22 ?0次下載

    深入淺出談多層面板布線技巧

    深入淺出談多層面板布線技巧
    發(fā)表于 12-13 22:20 ?0次下載

    深入淺出Android—Android開(kāi)發(fā)經(jīng)典教材

    深入淺出Android—Android開(kāi)發(fā)經(jīng)典教材
    發(fā)表于 10-24 08:52 ?15次下載
    <b class='flag-5'>深入淺出</b>Android—Android開(kāi)發(fā)經(jīng)典教材

    深入淺出數(shù)字信號(hào)處理

    深入淺出數(shù)字信號(hào)處理
    發(fā)表于 12-07 20:14 ?637次閱讀

    深入淺出學(xué)習(xí)250個(gè)通信原理資源下載

    深入淺出學(xué)習(xí)250個(gè)通信原理資源下載
    發(fā)表于 04-12 09:16 ?30次下載