欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Linux中的進(jìn)程地址空間用法解讀

 更新時(shí)間:2025年04月16日 09:02:42   作者:R_.L  
這篇文章主要介紹了Linux中的進(jìn)程地址空間用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

1.進(jìn)程地址空間分布

進(jìn)程地址空間分布圖

棧區(qū)是向地址減小方向開空間(棧是先使用高地址),而堆區(qū)是向地址增長(zhǎng)方向申請(qǐng)空間(堆是先使用低地址),堆棧之間的共享區(qū),主要用來加載動(dòng)態(tài)庫。

我們通過如下代碼來驗(yàn)證一下是否符合上面的地址空間分布:

#include<stdio.h>
#include<stdlib.h>
int g_unval;//未初始化
int g_val = 100;//初始化
int main(int argc, char* argv[], char* env[])
{
    const char* p = "hello bit";//p是指針變量(棧區(qū)),p指向字符常量h(字符常量區(qū))
    char* q = (char*)malloc(10);//q 存放堆區(qū)地址,&q 指的是棧區(qū)地址
    printf("env addr:            %p\n", env[0]);//環(huán)境變量
    printf("args addr            %p\n", argv[0]);//命令行參數(shù)

    printf("stack addr:          %p\n", &p);//p先定義,先入棧
    printf("stack addr:          %p\n", &q);//棧區(qū)
 
     
    printf("heap addr:           %p\n", q); //堆區(qū)
    printf("global val:          %p\n", &g_val); //全局初始化
    printf("global uninit val:   %p\n", &g_unval); //全局未初始化
    printf("read only :          %p\n", p);   //字符常量區(qū)
    printf("code addr:           %p\n", main);//代碼區(qū)起始地址
    return 0;
}

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

地址自上而下,可以看到它的地址分布嚴(yán)格遵守上面的進(jìn)程地址分布圖

進(jìn)程地址空間,會(huì)在進(jìn)程的整個(gè)生命周期內(nèi)一直存在,直到進(jìn)程退出,所以全局變量會(huì)一直存在。

2. 地址空間是虛擬的

進(jìn)程地址空間不是實(shí)際的物理內(nèi)存而是一個(gè)虛擬的地址內(nèi)存

我們?nèi)绾悟?yàn)證這個(gè)觀點(diǎn)呢??通過如下代碼:

#include<stdio.h>
#include<unistd.h>
int g_val = 10;
int main()
{
    printf("剛開始時(shí)的g_val:%d\n", g_val);
    pid_t id = fork();
    if (id == 0)
    {
        //子進(jìn)程
        g_val = 100;
        printf("子進(jìn)程中的g_val:%d\n", g_val);
        printf("子進(jìn)程中g(shù)_val的地址:%p\n", &g_val);

    }
    else if (id > 0)
    {
        //父進(jìn)程
        printf("父進(jìn)程中的g_val:%d\n", g_val);
        printf("父進(jìn)程中g(shù)_val的地址:%p\n", &g_val);

    }
    return 0;
}

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

可以看到這里父子進(jìn)程中g(shù)_val的地址是相同的,而g_val的值卻是不同的。 

根據(jù)常識(shí)我們都知道,一個(gè)地址不可能存放有兩個(gè)不同的數(shù)據(jù),因此我們可以得出進(jìn)程地址空間中的地址并不是真實(shí)的物理內(nèi)存地址,而是一個(gè)虛擬地址?。?/p>

虛擬地址是由操作系統(tǒng)提供的,由馮諾依曼體系結(jié)構(gòu)我們知道任何數(shù)據(jù)在啟動(dòng)時(shí)必須加載到物理內(nèi)存,所以肯定需要將虛擬地址轉(zhuǎn)化成物理地址。因此操作系統(tǒng)需要將虛擬地址轉(zhuǎn)化成物理地址。

進(jìn)程地址空間是由操作系統(tǒng)虛擬出來的內(nèi)存, 那么操作系統(tǒng)是如何劃分進(jìn)程地址空間區(qū)域的??

進(jìn)程地址空間本質(zhì)上是一種數(shù)據(jù)結(jié)構(gòu),是多個(gè)區(qū)域的集合。在Linux內(nèi)核中,有這樣一個(gè)結(jié)構(gòu)體:struct mm_struct,在這個(gè)結(jié)構(gòu)體里面進(jìn)行區(qū)域的劃分(棧區(qū)堆區(qū)等區(qū)域)。

struct mm_struct
{
    //...

    unsigned long heap_start;//堆區(qū)
    unsigned long heap_end;

    unsigned long stack_start;//棧區(qū)
    unsigned long stack_end;
    
    unsigned long uninit_start;//未初始化區(qū)
    unsigned long uninit_end;

    unsigned long init_start;//初始化區(qū)
    unsigned long init_end;

    unsigned long code_start;//代碼區(qū)
    unsigned long code_end;
    //...
}

這里還存在一個(gè)疑問??假設(shè)物理內(nèi)存是4G的情況下,那么每一個(gè)進(jìn)程中都有一個(gè)4G的進(jìn)程地址空間,我們都知道一個(gè)內(nèi)存可以加載多個(gè)進(jìn)程,那么物理內(nèi)存足夠給每一個(gè)進(jìn)程中的進(jìn)程地址空間的地址數(shù)據(jù)進(jìn)行映射存放下來嗎?

答案是夠的,每一個(gè)進(jìn)程都認(rèn)為他們會(huì)使用4G的物理內(nèi)存,但實(shí)際上他們所使用的空間遠(yuǎn)遠(yuǎn)小于4G的內(nèi)存,因此內(nèi)存中才能加載多個(gè)進(jìn)程。

3.虛擬地址和物理地址的映射

每一個(gè)進(jìn)程都會(huì)有一個(gè)PCB(task_struct結(jié)構(gòu)體),而PCB中有一個(gè)指針指向各自進(jìn)程的地址空間,進(jìn)程將自己的代碼和數(shù)據(jù)首先放在虛擬地址空間的對(duì)應(yīng)的區(qū)域,在這其中會(huì)有一種表結(jié)構(gòu),叫做頁表,頁表的核心工作就是完成虛擬地址到物理地址之間的映射。之后,我們的代碼和數(shù)據(jù)通過頁表的映射加載到實(shí)際的物理內(nèi)存中。

因此通過頁表建立的虛擬與物理地址之間的對(duì)應(yīng)關(guān)系成功將進(jìn)程中的代碼和數(shù)據(jù)存入到了內(nèi)存中

這里我們回答幾個(gè)問題。

1.不同進(jìn)程的虛擬地址可以一樣嗎??

可以的,因?yàn)椴煌M(jìn)程之間的虛擬地址可以是一樣的,每一個(gè)進(jìn)程都有獨(dú)屬于自己的頁表,他們通過頁表映射到不同的物理地址。

2.不同進(jìn)程的虛擬地址在頁表映射的物理地址是否會(huì)重復(fù)??

答案是不會(huì)的。但是存在著一種特殊情況,當(dāng)父進(jìn)程創(chuàng)造子進(jìn)程,子進(jìn)程會(huì)以父進(jìn)程的地址空間和頁表的映射等為模板,創(chuàng)造屬于自己的進(jìn)程地址和頁表,當(dāng)子進(jìn)程不對(duì)代碼和數(shù)據(jù)做改變時(shí),子進(jìn)程的頁表還是會(huì)指向和父進(jìn)程一樣的物理地址。

但是當(dāng)子進(jìn)程對(duì)數(shù)據(jù)修改時(shí),操作系統(tǒng)會(huì)在物理內(nèi)存中進(jìn)行寫實(shí)拷貝,開辟出一個(gè)新的物理地址,里面存放子進(jìn)程修改后的數(shù)據(jù),并且此時(shí)頁表會(huì)更新映射的物理地址。(這就回答了上面父子進(jìn)程中為什么g_val的值不同,而進(jìn)程地址相同的原因)

4.地址空間和頁表存在的意義

1.保護(hù)物理內(nèi)存,維持進(jìn)程的獨(dú)立性

如果進(jìn)程直接訪問物理內(nèi)存,若進(jìn)程中存在代碼問題(如指針越界等等),那么這個(gè)進(jìn)程很可能會(huì)訪問到別的進(jìn)程的數(shù)據(jù)并對(duì)該數(shù)據(jù)進(jìn)行修改,這就破壞了進(jìn)程的獨(dú)立性。而有了虛擬地址之后,通過頁表只能訪問自己映射到的物理內(nèi)存保證了進(jìn)程的獨(dú)立性。

2.頁表可以進(jìn)行越界行為的檢查

  • 第一種檢查,通常為指針越界的檢查 :

當(dāng)發(fā)生越界行為,系統(tǒng)會(huì)檢查越界后的地址地址是否在對(duì)應(yīng)的區(qū)域(比如指針原本是棧區(qū),越界后依然在棧區(qū)),編譯器會(huì)通過mm_struct結(jié)構(gòu)體里面該區(qū)域的范圍比對(duì),如果還在對(duì)應(yīng)的區(qū)域那么編譯器會(huì)認(rèn)為合法,如果不在則非法。

  • 第二種檢查,檢查是否對(duì)常量區(qū)的數(shù)據(jù)進(jìn)行修改:

其實(shí)頁表也有一種權(quán)限管理判斷是否可以讀寫,當(dāng)你對(duì)數(shù)據(jù)區(qū)進(jìn)行映射時(shí),數(shù)據(jù)區(qū)是可以讀寫的,相應(yīng)的在頁表中的映射關(guān)系中的權(quán)限就是可讀可寫,但是當(dāng)你對(duì)代碼區(qū)和字符常量區(qū)進(jìn)行映射時(shí),因?yàn)檫@兩個(gè)區(qū)域是只讀的,相應(yīng)的在頁表中的映射關(guān)系中的權(quán)限就是只讀,如果你對(duì)這段區(qū)域進(jìn)行了寫,通過頁表當(dāng)中的權(quán)限管理,操作系統(tǒng)就直接就將這個(gè)進(jìn)程干掉。

3.降低內(nèi)存和進(jìn)程管理的耦合

  • 若沒有進(jìn)程地址空間,當(dāng)進(jìn)程退出時(shí),內(nèi)存管理需要盡快對(duì)該進(jìn)程回收釋放,而有進(jìn)程加載到內(nèi)存時(shí),內(nèi)存管理又要及時(shí)分配資源,耦合度太高。
  • 當(dāng)有了進(jìn)程地址空間后,一個(gè)進(jìn)程需要資源的時(shí)候,通過頁表映射去要就即可。
  • 內(nèi)存管理就只需要知道哪些內(nèi)存區(qū)域(配置)是無效的,哪些是有效的(被頁表映射的就是有效的,沒有被頁表映射的就是無效的)。
  • 當(dāng)進(jìn)程退出,頁表也隨之退出,沒有了映射關(guān)系后,物理內(nèi)存將該進(jìn)程的數(shù)據(jù)映射的物理地址設(shè)置為無效即可。同理有進(jìn)程進(jìn)入時(shí)設(shè)置為有效。

物理內(nèi)存如何知道某個(gè)地址是否被映射(有效)??

可以理解為在每個(gè)物理內(nèi)存的地址出有一個(gè)計(jì)數(shù)器,當(dāng)計(jì)數(shù)器為1,即該處被映射,為0時(shí)則沒被映射。

4.內(nèi)存管理如何將一些大型數(shù)據(jù)加載到物理內(nèi)存 

內(nèi)存管理是通過延遲加載的方式加載到物理內(nèi)存的。一個(gè)大型進(jìn)程,內(nèi)存管理首先會(huì)給你加載小一部分先供你使用,當(dāng)你使用完時(shí),會(huì)先將進(jìn)程置為睡眠狀態(tài),然后將進(jìn)程再喚醒,再加載一部分?jǐn)?shù)據(jù)代碼,進(jìn)程再繼續(xù)使用,依次反復(fù)。對(duì)于用戶來說,唯一感覺到的是我的游戲運(yùn)行的慢了。

頁表中還有一個(gè)管理權(quán)限判斷判斷進(jìn)程的代碼和數(shù)據(jù)是否已經(jīng)加載到了內(nèi)存,0為沒有加載到內(nèi)存,而1則相反,這只是一個(gè)簡(jiǎn)化理解

磁盤會(huì)將這個(gè)大型可執(zhí)行程序的數(shù)據(jù)和代碼一點(diǎn)點(diǎn)的放入到內(nèi)存當(dāng)中,而操作系統(tǒng)對(duì)物理內(nèi)存的分配是:進(jìn)程用到哪里就加載多少數(shù)據(jù)代碼原則。

進(jìn)程所有的代碼和數(shù)據(jù)都有對(duì)應(yīng)的虛擬地址,因此對(duì)于那些還不需要用到的數(shù)據(jù)代碼,頁表會(huì)在對(duì)應(yīng)權(quán)限標(biāo)0表示沒有加載到內(nèi)存,當(dāng)需要這個(gè)數(shù)據(jù)代碼的時(shí)候,操作系統(tǒng)會(huì)在物理內(nèi)存里開辟空間,并且訪問磁盤里對(duì)應(yīng)的代碼和數(shù)據(jù)。

這些理解都是一些比較淺的理解,對(duì)于頁表的具體理解,需要后面進(jìn)行更深的理解。

最后還有兩個(gè)問題,CPU如何訪問進(jìn)程的頁表??CPU對(duì)頁表做出修改后,進(jìn)程如何保持更新呢??

頁表的地址屬于進(jìn)程的上下文。

CPU中有一個(gè)叫做cr3的寄存器存放著頁表的地址(可以把頁表想象成unordered_map結(jié)構(gòu)的對(duì)象),CPU也是通過該地址訪問頁表的,當(dāng)進(jìn)程運(yùn)行完后,進(jìn)程會(huì)帶走自己的臨時(shí)數(shù)據(jù),也就是進(jìn)程的上下文,所以進(jìn)程也會(huì)帶走頁表的地址,這樣就做到了保持更新。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Apache壓力測(cè)試工具的安裝使用

    Apache壓力測(cè)試工具的安裝使用

    這篇文章主要介紹了Apache壓力測(cè)試工具—安裝并進(jìn)行并發(fā)接口測(cè)試的實(shí)現(xiàn),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • Apache網(wǎng)頁的優(yōu)化、安全與防盜鏈圖文詳解

    Apache網(wǎng)頁的優(yōu)化、安全與防盜鏈圖文詳解

    防盜鏈就是防止別人的網(wǎng)站代碼里面盜用服務(wù)器的圖片、文件、視頻等相關(guān)資源,下面這篇文章主要給大家介紹了關(guān)于Apache網(wǎng)頁的優(yōu)化、安全與防盜鏈的相關(guān)資料,需要的朋友可以參考下
    2022-10-10
  • Ubuntu16.04 靜態(tài)IP地址設(shè)置(NAT方式)

    Ubuntu16.04 靜態(tài)IP地址設(shè)置(NAT方式)

    這篇文章主要介紹了Ubuntu16.04 靜態(tài)IP地址設(shè)置(NAT方式),詳細(xì)的介紹了為VMware虛擬機(jī)內(nèi)安裝的Ubuntu 16.04設(shè)置靜態(tài)IP地址NAT方式,有興趣的可以了解一下。
    2017-02-02
  • linux下使用cmake編譯安裝mysql的詳細(xì)教程

    linux下使用cmake編譯安裝mysql的詳細(xì)教程

    這篇文章主要介紹了linux下使用cmake編譯安裝mysql的詳細(xì)教程,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-10-10
  • Linux服務(wù)器上安裝JDK全過程

    Linux服務(wù)器上安裝JDK全過程

    文章內(nèi)容總結(jié):本文詳細(xì)介紹了在Linux服務(wù)器上安裝Java的步驟,包括下載JDK、上傳服務(wù)器、解壓縮、配置環(huán)境變量等,并提供了一個(gè)簡(jiǎn)單的命令來驗(yàn)證安裝是否成功,希望本文能為讀者提供有用的參考
    2024-11-11
  • 詳解在Linux下9個(gè)有用的touch命令示例

    詳解在Linux下9個(gè)有用的touch命令示例

    本篇文章主要介紹了詳解在Linux下9個(gè)有用的touch命令示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-05-05
  • Apache服務(wù)的主要目錄和配置文件詳解

    Apache服務(wù)的主要目錄和配置文件詳解

    這篇文章主要介紹了Apache服務(wù)的主要目錄和配置文件詳解,需要的朋友可以參考下
    2017-05-05
  • Linux 下安裝pip包的方法

    Linux 下安裝pip包的方法

    這篇文章主要介紹了Linux 下安裝pip包的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-06-06
  • Centos7實(shí)現(xiàn)磁盤限額設(shè)置方法

    Centos7實(shí)現(xiàn)磁盤限額設(shè)置方法

    本篇文章給大家詳細(xì)分享了Centos7實(shí)現(xiàn)磁盤限額設(shè)置方法,對(duì)此有需要的朋友可以參考學(xué)習(xí)下。
    2018-02-02
  • Linux出現(xiàn)sql密碼被忘記的解決方法

    Linux出現(xiàn)sql密碼被忘記的解決方法

    我們?cè)贚inux系統(tǒng)中使用Mysql數(shù)據(jù)庫時(shí),有時(shí)會(huì)將密碼忘記,無法進(jìn)行登陸,所以本文小編給大家大家介紹了Linux出現(xiàn)sql密碼被忘記的解決方法,文中通過圖文講解的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下
    2024-08-08

最新評(píng)論