首先引入地址空間的作用
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4
int g_val = 100;
6 int main()
7 {
8 pid_t id = fork();
9 if(id == 0)
10 {
11 int cnt = 0;
12 while(1)
13 {
14 printf("I am child,pid : %d,ppid : %d,g_val : %d,&g_val : %p\n",getpid(),getppid(),g_val,&g_val);
15 cnt++;
16 sleep(1);
17 if(cnt == 5)
18 {
19 g_val = 200;
20 printf("child chage g_val 100 -> 200 success\n");
21 }
22 }
23 }
24 else
25 {
26 //father
27 while(1)
28 {
29
30 printf("I am father,pid : %d,ppid : %d,g_val : %d,&g_val : %p\n",getpid(),getppid(),g_val,&g_val);
31 sleep(1);
32 }
33 }
34 return 0;
35 }
我們發(fā)現(xiàn),但我們子進(jìn)程修改全局變量g_val的時候,父進(jìn)程的g_val沒有受到影響,但是他們的地址都是一樣的,這是為什么呢?
由此我們知道,這里的地址絕對不是物理內(nèi)存的地址,而是虛擬地址(線性地址);并且?guī)缀跛姓Z言,如果有地址的概念,這個地址一定不是物理地址,而是虛擬地址。物理地址是由操作系統(tǒng)保管的。以下我們就開始介紹虛擬內(nèi)存的作用
什么是地址空間
首先基本了解一下地址空間的排布情況
目前我們先不考慮解析這里的共享區(qū)
代碼實現(xiàn)驗證地址空間的排布
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4 int g_val = 100;
5 int g_unval;
6 int main(int argc,char *argv[],char *env[])
7 {
8 //代碼區(qū)
9 printf("code addr:%p\n",main);
10 //初始化數(shù)據(jù)
11 printf("init global addr:%p\n",&g_val);
12 //未初始化數(shù)據(jù)
13 printf("uninit global addr:%p\n",&g_unval);
14 //堆區(qū)
15 char* heap_mem = (char*)malloc(10);
16 char* heap_mem1 = (char*)malloc(10);
17 char* heap_mem2 = (char*)malloc(10);
18 char* heap_mem3 = (char*)malloc(10);
19 printf("heap_mem addr:%p\n",heap_mem);
20 printf("heap_mem1 addr:%p\n",heap_mem1);
21 printf("heap_mem2 addr:%p\n",heap_mem2);
22 printf("heap_mem3 addr:%p\n",heap_mem3);
23
24 //棧區(qū)
25 printf("stack addr:%p\n",&heap_mem);
26 printf("stack addr:%p\n",&heap_mem1);
27 printf("stack addr:%p\n",&heap_mem2);
28 printf("stack addr:%p\n",&heap_mem3);
29 //字面常量
30 const char *str = "helloworld";
31 printf("read only string addr: %p\n", str);
32
33 int i,j;
34 //命令區(qū)
35 for(i = 0 ;i < argc; i++)
36 {
37 printf("argv[%d]: %p\n", i, argv[i]);
38 }
39
40 //環(huán)境區(qū)
41 for(j = 0;env[j];++j)
42 {
43 printf("env[%d] addr:%p\n",j,&env[j]);
44 }
45
46
47
48 return 0;
49 }
由此可見我們發(fā)現(xiàn)我們輸入命令后,命令的地址在我們所執(zhí)行的代碼之后,這說明剛創(chuàng)建好這些變量就有了它自己本身的地址,地址程序結(jié)束后才打印,要分清前后
接下來我們來認(rèn)識什么是地址空間
這時我們可以利用虛擬地址加映射機(jī)制(頁表)來正確的講地址存入物理內(nèi)存
虛擬地址:不管哪個編譯器,只要看到的地址都是虛擬地址,物理地址是操作系統(tǒng)保管的。
每一行代碼都進(jìn)行了編址。故,程序在編譯的時候,每一個字段早已經(jīng)具有了一個虛擬地址
=什么是映射機(jī)制?
映射機(jī)制可以將虛擬地址轉(zhuǎn)換到物理地址,如果發(fā)現(xiàn)虛擬地址會越界或者錯誤,則就不會拋出,他起到了關(guān)鍵作用
那么映射機(jī)制是怎么判斷的呢?
以上就是所描述的社么是地址空間,簡單來說它是存儲虛擬地址的。
地址空間和頁表(用戶級)是每一個進(jìn)程都單獨(dú)有一份的。
只要每一個進(jìn)程的頁表映射的是物理內(nèi)存的不同區(qū)域,就可以做到進(jìn)程之間不會互相干擾保證進(jìn)程的獨(dú)立性。
為什么要有地址空間
- 凡是非法的訪問或者映射,os都會識別到,并終止你這個進(jìn)程,有效的保護(hù)了物理內(nèi)存。
因為地址空間和頁表是os創(chuàng)建并維護(hù)的,所以凡是使用地址空間和頁表的都會在os的監(jiān)控下來進(jìn)行范文,這樣就間接的保護(hù)了物理內(nèi)存中的所有合法數(shù)據(jù)和各個進(jìn)程,以及內(nèi)核的相關(guān)有效數(shù)據(jù)- 物理內(nèi)存和進(jìn)程的管理可以做到解耦合(沒關(guān)聯(lián))。
當(dāng)我們申請了物理空間,但是我們不立即使用的時候,就會造成內(nèi)存空間的浪費(fèi);
針對這一現(xiàn)象,os做出了延遲分配的策略,來提高整機(jī)的效率。
因為地址空間的存在,所有申請的空間都是在地址空間上申請的,物理內(nèi)存不是被申請到一個字節(jié),當(dāng)我們真正訪問物理地址的時候,才執(zhí)行內(nèi)存相關(guān)的算法。幫助申請內(nèi)存,構(gòu)建頁表之間的映射關(guān)系,這些都是由os自主完成的- 因為在物理內(nèi)存中理論上隨意加載,也是隨意存放的,但是通過地址空間的虛擬地址和頁表之間的映射,從進(jìn)程視角來看所有的內(nèi)存分布就成有序的了。
因為有地址空間的存在,每一個進(jìn)程都認(rèn)為自己單獨(dú)有一塊4GB(32)空間,并且各個區(qū)域是有序的。進(jìn)而通過頁表映射到不同區(qū)域,來實現(xiàn)進(jìn)程的獨(dú)立性,各個進(jìn)程是不知道其他進(jìn)程的存在的
回答問題,為什么地址相同值不同文章來源:http://www.zghlxwxcb.cn/news/detail-645848.html
發(fā)生了寫時拷貝!,所以父子進(jìn)程各自其實在物理內(nèi)存中,有屬于自己的變量空間!只不過在用戶層用同一個變量(虛擬地址!)來標(biāo)識了文章來源地址http://www.zghlxwxcb.cn/news/detail-645848.html
到了這里,關(guān)于【Linux】程序地址空間的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!