進程地址空間
例子引入:
我們在講C語言的時候,老師給大家畫過這樣的空間布局圖,但是我們對它不了解
我們寫一個代碼來驗證Linux進程地址空間
#include<stdio.h>
#include<assert.h>
#include<unistd.h>
int g_value=100;
int main()
{
pid_t id=fork();
assert(id>=0);
if(id==0)
{
//child
while(1)
{
printf("我是子進程,我的id是:%d,我的父進程是:%d, g_value:%d,&g_value:%p\n",getpid(),getppid(),g_value,&g_value);
sleep(1);
}
}
else
{
//father
while(1)
{
printf("我是父進程,我的id是:%d,我的父進程是:%d,g_value:%d,&g_value:%p\n",getpid(),getppid(),g_value,&g_value);
sleep(2);
}
}
return 0;
}
這里沒什么問題,就是他們的g_valule 和其地址都是一樣的,
我們將代碼調整一下,讓子進程的g_value++
#include<stdio.h>
#include<assert.h>
#include<unistd.h>
int g_value=100;
int main()
{
pid_t id=fork();
assert(id>=0);
if(id==0)
{
//child
while(1)
{
printf("我是子進程,我的id是:%d,我的父進程是:%d,g_value:%d,&g_value:%p\n",getpid(),getppid(),g_value,&g_value);
sleep(1);
g_value++;//只有子進程會進行修改
}
}
else
{
//father
while(1)
{
printf("我是父進程,我的id是:%d,我的父進程是:%d,g_value:%d,&g_value:%p\n",getpid(),getppid(),g_value,&g_value);
sleep(2);
}
}
return 0;
}
我們可以發(fā)現(xiàn)子進程的g_value變了,但是父進程沒有變,兩個的地址還是一樣的
?為什么他們兩個地址相同但是讀出來的數(shù)據不同呢?(下文會解答)
??子進程對全局數(shù)據修改,并不影響父進程!——進程具有獨立性!
?這個地址會是物理地址???不會
顯然這個地址絕對不是物理地址!所以我們平常在語言層面用的地址,絕對不是物理地址,所以以前用的指針絕對不是地址,其實這個地址叫做虛擬地址or線性地址
故事引入:
香港某個老板非常滴有錢,有10億美金,他有 4個私生子,每個私生子都并不知道對方的存在,他們都以為自己是獨生子。因為他們彼此不知道對方的存在,所以他們在生活和工作上也沒有交集,不會有任何互相的影響(這就是獨立性的體現(xiàn))。財閥老板為了維護自己的獨立性:
他就對大兒子說:“兒子,你好好學習,以后老爹錢都是你的?!保髢鹤右宦犈P槽真好,高枕無憂,就好好學習,一想到自己以后有錢,就更想學習了。
然后又對二兒子說:“兒子,好好工作,等以后我就把公司給你?!保鹤右宦牊釡I盈眶,于是就好好工作,等著將來有一天可以繼承公司。
后來又對三兒子說:“兒子,你好好干活,等你長大老爹的家產交給你!”,三兒子知道自己以后會繼承老爹的所有財產,開心壞了,就努力的干活。
后來又對四兒子說:“兒子,你好好干活,等你長大老爹的家產交給你!”,四兒子知道自己以后會繼承老爹的所有財產,開心壞了,就努力的干活。
只要在財閥爹的可承受范圍內,孩子要多少錢他都給多少錢,所以三個兒子自然都認為自己有很多錢。財閥老板給他的三個兒子畫了一張?zhí)摂M的、不存在的大餅,讓他們都能努力學習工作干活(這個步驟就是給他們分別建立了進程地址空間)。
畫的餅:進程地址空間,10億美金:內存,老板:操作系統(tǒng),四個私生子是進程
?大富翁,要不要把“餅”管理起來呢?
顯然需要的,遵循先描述再組織的原則
所以,進程地址空間,就是就是給進程畫的大餅
進程地址空間 → 邏輯上抽象的概念 → 讓每個進程都認為自己獨占系統(tǒng)的所有資源
**概念:**操作系統(tǒng)通過軟件的方式,給進程提供一個軟件視角,認為自己是獨占系統(tǒng)的所有資源(內存)。
區(qū)域和頁表:
什么叫做區(qū)域?我們來拿一張桌子來理解,初中的時候小花和小胖分過 “38線”
三八線的本質就是區(qū)域劃分!
??地址空間本身就是一個線性區(qū)域,地址空間是線性結構的!
struct mm_struct {
long code_start;
long code_end;
long init_start;
long init_end;
long uninit_start;
long uninit_end;
long heap_start;
long heap_end;
long stack_start;
long stack_end;
...
}
如果限定了區(qū)域,那么區(qū)域之間的數(shù)據是什么?
是虛擬地址or線性地址
??程序加載到內存,由程序變成進程后,由操作系統(tǒng)給每個進程構建的一個頁表結構,就是 頁表。
??數(shù)據和代碼真正只能在內存中!
找到地址不是目的,而是手段
回到之前那個問題:
?為什么他們兩個地址相同但是讀出來的數(shù)據不同呢?
??如果子進程對數(shù)據進行了修改,因為進程具有獨立性,子進程的修改不能影響父進程
子進程這里的 物理地址改了,但是虛擬地址沒有改
寫時拷貝發(fā)生在物理地址,虛擬地址沒有變
因為進程具有獨立性,比如如果此時子進程把變量改了(寫入),就會導致父進程識別的問題就出現(xiàn)了父進程和子進程不一的情況,因為進程是具有獨立性的,所以我們就要做到互不影響。我們的子進程要進行修改了,影響到父進程怎么辦?沒關系!操作系統(tǒng)會出手!當我們識別到子進程要修改時,操作系統(tǒng)會重新給子進程開辟一段空間,并且把 100 拷貝下來,重新給進程建立映射關系,所以子進程的頁表就不再指向父進程所對應的 100 了,而直接指向新的 100。你在做修改時又把它的值從 100 改成 200 時,我們就出現(xiàn)了 “改的時候永遠改的是頁表的右側,左側不變” 的情況,所以最后你看到了父子進程的虛擬地址一樣,但是經過頁表映射到了不同的物理內存,所以了你看到了一個是 100 一個是 200,父子進程的數(shù)據不同的結果。
我們的操作系統(tǒng)當我們的父子對數(shù)據進行修改時,操作系統(tǒng)會給修改的一方重新開辟一塊空間,并且把原始數(shù)據拷貝到新空間當中,這種行為就是 寫時拷貝!
當父子有任何一個進程嘗試修改對應變量時,有一個人想修改,就會觸發(fā)寫時拷貝,讓他去拷貝新的物理內存,這只需要重新構建也表的映射關系,虛擬地址是不發(fā)生任何變化的,所以最終你看的結果是虛擬地址不變,而內容不同。
這個結構也體現(xiàn)了進程具有獨立性
pid_t id=fork()
if(){}
else
{}
?fork在返回的時候,父子都有,return兩次,id是不是pid_T類型定義的變量呢?
??是的,返回的本質就是寫入!誰先返回,誰就讓OS發(fā)生寫時拷貝
如果是父進程就返回pid,如果是子進程就返回0
為什么進程地址空間要存在?
?如果沒有地址空間,我們OS是如何工作呢?
??這里就是害怕野指針的情況,要尋找一個地址因為你的代碼錯誤找到了一個越界地址時寫入時會使別人的進程錯了而且很不安全,因此有了頁表和虛擬空間
??這兩個存在的意義:1.防止地址隨意訪問,保護物理內存與其他進程
?常量字符串不能修改,這是為什么呢???因為頁表訪問的時候是有權限的,權限不能修改
char*str=“hello world”;
*str=‘H’;
??先來將另外一個擴充:malloc的本質——
?向OS申請內存,操作系統(tǒng)立馬給你,還是說在你需要的時候給你?
??1.在你需要的時候給你,OS一般不允許任何的浪費或者不高效
2.申請內存==立馬使用呢?不一定等于立馬使用
3.在你申請成功之后,和你使用之前就有一段小小的時間窗口,這個空間沒有被正常使用,但是別人用不了—-閑置狀態(tài)
??如果有500進程這樣的話,這樣操作系統(tǒng)就有大塊的空間處于這種狀態(tài),這種情況叫做缺頁中斷文章來源:http://www.zghlxwxcb.cn/news/detail-713578.html
?因為有頁表,你關心不關心你申請的空間是在物理空間的哪一塊呢???不關心,一樣的文章來源地址http://www.zghlxwxcb.cn/news/detail-713578.html
到了這里,關于【Linux進行時】進程地址空間的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!