Linux 进程(二) 进程地址空间
上一節(jié)我們提到過父子進(jìn)程的一個概念:父子進(jìn)程代碼共享,數(shù)據(jù)各自開辟空間。
因?yàn)樽舆M(jìn)程從父進(jìn)程的PCB中拷貝了數(shù)據(jù),所以它的代碼、數(shù)據(jù)以及運(yùn)行的位置,都與父進(jìn)程一模一樣。但是為什么這個代碼是無法修改的?為什么又需要再各自開辟空間呢?Linux是如何實(shí)現(xiàn)權(quán)限控制以及空間映射的呢?
我們用這段代碼進(jìn)行試驗(yàn)
#include<stdio.h> #include<unistd.h> #include<stdlib.h>int val = 0;int main() {pid_t id = fork();if(id == 0){val = 100;//子進(jìn)程printf("子進(jìn)程pid:[%d] val:[%d] val地址:[%p]\n", getpid(), val, &val);}else if(id > 0){//父進(jìn)程sleep(3);printf("父進(jìn)程pid:[%d] val:[%d] val地址:[%p]\n", getpid(), val, &val);}return 0;}我們利用一個全局變量val,看看修改子進(jìn)程中的變量val,父進(jìn)程會不會發(fā)生變化,他們的地址又是否相同。
因?yàn)樽舆M(jìn)程運(yùn)行的位置和父進(jìn)程一樣,所以先讓父進(jìn)程睡眠一會,讓子進(jìn)程先修改
奇怪的事情發(fā)生了,明明子進(jìn)程已經(jīng)修改了val,但是父進(jìn)程的卻沒變,同時明明父子進(jìn)程中全局變量val的大小都不一樣,發(fā)生了變化,但是他們的地址確還是一樣的,這就有些不符合邏輯了,因?yàn)橐粋€地址中不可能有兩個同名的變量。
這里就讓我們確定了一件事情,我們在代碼中所看到的地址,并不是真正的地址。
這就引入了程序地址空間的概念。
進(jìn)程地址空間
程序是不占用內(nèi)存的,它只是一個沒有生命的實(shí)體, 只有運(yùn)行起來的程序(進(jìn)程)才會被加載到內(nèi)存中,這才會占用內(nèi)存。
地址:地址就是對內(nèi)存單元的編號,通過這個編號來訪問數(shù)據(jù)。
從上面的例子我們發(fā)現(xiàn),代碼中看到地址并不是真正的內(nèi)存地址,而是虛擬內(nèi)存地址。
為什么要創(chuàng)建這樣一個虛擬的內(nèi)存地址呢?
操作系統(tǒng)為了不讓進(jìn)程直接訪問物理內(nèi)存,通過mm_struct結(jié)構(gòu)體來為進(jìn)程描述了一個虛擬的,連續(xù)的,完整的地址空間(只有編號,無法存儲),也就是我們所說的虛擬地址空間。
為什么不讓進(jìn)程直接訪問物理內(nèi)存呢?
假設(shè)我們內(nèi)存中有6m的空間,其中已經(jīng)存入了3m,這時我們想再存入一個3m,但是問題來了,因?yàn)槲锢砜臻g還剩下的3m是不連續(xù),所以這時會再找一個連續(xù)的空間來存儲這個3m。這樣就造成了內(nèi)存的大量浪費(fèi)。
還有這樣一種情況。
當(dāng)幾個進(jìn)程同時訪問物理內(nèi)存時,各進(jìn)程的操作可能會產(chǎn)生沖突,可能會產(chǎn)生無法預(yù)料的后果。缺乏訪問控制的內(nèi)存是非常不安全的。
頁表
操作系統(tǒng)再引入虛擬地址空間的時候還引入了一種東西,叫做頁表。
通過頁表來映射虛擬地址和物理地址的關(guān)系。
-
通過在虛擬地址來使數(shù)據(jù)進(jìn)行連續(xù)的存儲,然后再通過頁表映射到物理內(nèi)存上,來實(shí)現(xiàn)離散式的存儲,提高了內(nèi)存的利用率。
-
同時頁表可以針對某個地址設(shè)置訪問權(quán)限,讓某個地址設(shè)置為只讀,通過這種方法來實(shí)現(xiàn)內(nèi)存的訪問控制。
-
為了能夠使進(jìn)程具有獨(dú)立性,彼此之間不會相互干預(yù),每一個進(jìn)程都會有它自己的頁表和虛擬地址空間。
回到最開始的問題。
為什么父子進(jìn)程的代碼相同,且無法修改?
:因?yàn)橥ㄟ^頁表將代碼段的權(quán)限設(shè)置為只讀,所以無法修改。
為什么父子進(jìn)程數(shù)據(jù)各自開辟空間?
:其實(shí)父子進(jìn)程一開始物理地址和虛擬地址都是相同的,但是當(dāng)任意一個進(jìn)程中數(shù)據(jù)發(fā)生變化的時候,這個時候操作系統(tǒng)會找到另外一塊物理空間,將數(shù)據(jù)全部拷貝過去給發(fā)生修改的進(jìn)程使用,并且修改原來的物理空間的權(quán)限,使原來的物理空間給另一個進(jìn)程使用。
總結(jié)
以上是生活随笔為你收集整理的Linux 进程(二) 进程地址空间的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux 进程(一) 进程概念和进程状
- 下一篇: Linux 进程控制 :进程创建,进程终