日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux系统编程14:进程入门之Linux进程中非常重要的概念之进程地址空间-原来我们看到的地址全部是虚拟的

發布時間:2025/3/15 linux 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux系统编程14:进程入门之Linux进程中非常重要的概念之进程地址空间-原来我们看到的地址全部是虚拟的 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • (1)舊知回顧
    • (2)程序地址空間?
      • A:同一個地址有兩個數據?
      • B:物理地址和虛擬地址
      • C:進程地址空間及作用
      • D:進程地址空間如何工作

(1)舊知回顧

學習C/C++總免不了這張圖

這張圖幫助我們解決了不少C/C++問題,但是我們對它的認識還是不夠深刻。現在我我們用一端C程序,深刻的取感受一下這個過程的內存地址的變化

#include <stdio.h> #include <stdlib.h>int gobal_val=100;//全局變量已經初始化 int gobal_unval;//全局變量未初始化 int main(int argc,char* argv[],char* env[]) {printf("main函數處于代碼段,地址為:%p,十進制為:%d\n",main,main);printf("\n");printf("全局變量gobal_val,地址為:%p,十進制為:%d\n",&gobal_val,&gobal_val);printf("\n");printf("全局變量未初始化gobal_unval,地址為:%p,十進制為:%d\n",&gobal_unval,&gobal_unval);printf("\n");char* mem=(char*)malloc(10);printf("mem開辟的堆空間,mem是堆的起始地址,是%p,十進制為:%d\n",mem,mem);printf("\n"); printf("mem是指針變量,指針變量在棧上開采,其地址為%p,十進制為:%d\n",&mem,&mem);printf("\n"); printf("命令行參數起始地址:%p,十進制為:%d\n",argv[0],argv[0]);printf("\n");printf("命令行參數結束地址:%p,十進制為:%d\n",argv[argc-1],argv[argc-1]);printf("\n");printf("第一個環境變量的地址:%p,十進制為:%d\n",env[0],env[0]);printf("\n"); }

(2)程序地址空間?

上面的圖在學習C/C++時我們稱之為程序的地址空間分布圖,說白了就是一段程序在其運行過程中的數據的內存分布情況,但是接下來的敘述可能和你所認為的有所出入

A:同一個地址有兩個數據?

如下C程序,有個全局變量val,初始值為0,使用fork創建一個子進程,然后讓父進程先睡眠三秒,先讓子進程運行,子進程運行時把val改為100,三秒后父子進程同時運行

#include <stdio.h> #include <stdlib.h> #include <unistd.h>int val=0; int main() {pid_t id=fork();if(id==0){val=100;while(1){printf("現在是子進程:val=%d,其地址為%p\n",val,&val);sleep(1);}}else if(id>0){sleep(3);while(1){printf("############################################\n")printf("現在是父進程:val=%d,其地址為%p\n",val,&val);sleep(1);}}else{exit(-1);}sleep(1);return 0;}

效果如下,問題就在于同一個變量怎么會同時具有兩個不同的值?

B:物理地址和虛擬地址

我們能夠很明確一點:同一片空間不可同時具有兩個值,就像現實生活中,你不可能同時再學校又同時在家。那么只有一種解釋:你所看到并不是真實的,也就是說你所看到的地址并不是真實的物理地址,而是表象上的相同,他們對應的真實的物理地址肯定是不相同的,我們稱這種地址為虛擬地址

實際上:我們使用C/C++看到的地址,全部都是虛擬地址,真實的地址用戶看不到,而操作系統就負責將虛擬地址轉換為物理地址

可以這樣描述:父進程啟動(它的內存空間也是虛擬的不是真實的),然后遇見fork就創建了一個子進程,fork就把父進程的內存狀態拷貝了給自己一份,如果val的值沒有改變,那么操作系統本著不浪費一點空間原則,直接使用val這個虛擬地址所對應的物理地址就可以了。但是好景不長,子進程把val修改掉了,所以操作系統就把子進程val的虛擬地址所對應的物理地址進行了修改。從表面看起來好似地址沒有變,但是真實情況早都變化了。

C:進程地址空間及作用

1:早期直接訪問物理內存的缺陷所在

我們知道進程=進程控制塊PCB+代碼+數據,早期計算機,也就是沒有進程地址空間的時候進程一旦啟動,這些東西就會被全部裝入內存,那么此時進程訪問的就是真實的物理內存,如果畫一張圖,應該就是下面這樣

但是這樣防止有很大壞處:比如說野指針,放的這么近,一旦指針訪問的別的進程的數據,這就出了大亂子了。還有進程在運行過程中,是會產生數據的,產生的數據一旦不能放在本進程后面,就要另外找內存去存放,這樣就會導致不連續的現象,也增加了異常訪問的情況

2:進程地址空間的發明

所以計算機設計者意識到了這種模式缺陷,想到了一種方法:增加一個中間層,利用中間層映射物理內存。程序訪問內存時不直接訪問物理內存,先訪問中間層,如果中間層訪問沒有問題,那么操作系統就會將中間層映射到物理層,完成正常執行

一個進程創建之后,操作系統會為這個進程分配一個專屬于它的大小為4GB的虛擬進程地址空間(4GB是因為32位系統中,指針是4個字節),與它相對的是一片真實的物理地址空間,操作系統在映射虛擬內存時只會映射到那一片物理空間,而且需要特別注意這個虛擬空間并不是真的有4GB,它只是虛擬的 。由于每一個進程都有自己的虛擬的進程地址空間,所以它只能訪問自己的進程的數據,這樣做實現了隔離,也就是進程之間的相互獨立。并且把虛擬地址空間劃分為這樣,那樣的區,這樣的話也能解決數據的連續存放

3:頁表
不管怎樣,程序運行一定要在物理內存上,所以如何映射成為了關鍵,,這種映射稱為頁表
映射時,讓虛擬地址和物理地址一一對應,進程在虛擬地址上的地址就對應了物理內存上的某個地址。這樣的話就不存在非法訪問了,因為每個進程都有自己獨立的進程地址空間,如果非法訪問,頁表上根本就找不到這樣的地址,所以操作系統拒絕映射。“代碼,字符串是只讀的,數據是可讀可寫的”就是基于這個原因,雖然咋頁表上能找到,但是對代碼,字符串做了權限限制,一旦監測到這些數據類型要做一些寫入操作,那么同樣操作系統拒絕映射

D:進程地址空間如何工作

還是那個觀點“先描述,再組織”,也就是說我們看見的那個經典的棧,堆分布圖,其實本質也是一個結構體,這個結構體隸屬于task_struct,叫做task mm_struct(文件在mm_types.h)


這張圖可以說明task_stuct和task mm_struct的關系

申請空間的本質就是想內存索要空間,得到物理地址,然后在特定區域申請沒有使用的虛擬地址,建立映射關系,再返回虛擬地址

總結

以上是生活随笔為你收集整理的Linux系统编程14:进程入门之Linux进程中非常重要的概念之进程地址空间-原来我们看到的地址全部是虚拟的的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。