fork、父進程和子進程

進程

什麼是進程?進程是一個運行中的程序實體,擁有獨立的堆棧、內存空間和邏輯控制流。函數

這是標準的進程概念。讓咱們經過操做系統的fork函數看看這個抽象的概念是怎麼在進程的實現中體現出來的。操作系統

構成要素

建立一個進程,須要進程體、進程表和數據空間。指針

進程體在C代碼中對應一個函數,編譯成二進制代碼後就是一組指令。code

進程表用來記錄進程的進程ID、進程名稱、寄存器快照空間。簡單說,當中斷髮生時,會保存此刻CPU的狀態,而後記錄到進程表中。進程

進程表的做用就是用來存儲進程快照。內存

進程堆棧的做用是什麼?存儲進程中函數的參數,存儲進程運行過程當中的局部數據。字符串

數據空間呢?先看一段簡單的代碼。編譯

char *f(int a, int b);

int main(int argc, char **argv)
{
  	f(5, 6);
  
  	return 0;
}

char *f(int a, int b)
{
  	int c = a + b;
  	char *str = "Hello, World!";
  	return str;
}
  1. 兩個參數a和b存儲在進程的堆棧中。
  2. 指針char *str指向的內存中的數據STR存儲在進程的數據空間中。

爲何STR不是存儲在進程的堆棧中呢?class

函數f的返回值是STR的內存地址。執行這段代碼,咱們會發現:調用函數f能正確得到STR。變量

試想一下,假如STR存儲在進程的堆棧中,當f執行結束後,堆棧中的數據會被清空,咱們調用函數f是不能正確得到STR的。

STR存儲在進程的數據空間中,存儲在進程堆棧中的只是存儲STR的內存空間的內存地址。

fork

進程A調用fork新建進程B,A是B的父進程,B是A的子進程。

fork執行結束後,若是能成功建立B進程,B進程的數據空間、堆棧和進程表和A進程的這些要素徹底相同。

差別

B進程畢竟是不一樣於A進程的獨立進程,因此:

  1. B進程的數據空間中的數據和A進程的數據空間的數據一致,可是,兩個進程的數據空間倒是不一樣的內存空間。
  2. B進程表中,指向LDT的選擇子和A進程表中的LDT選擇子不一樣。
  3. B進程表中的進程ID和A進程表中的進程ID不一樣。

堆棧

猜猜看,子進程的堆棧是在進程表中仍是在數據空間中?

回答是:在進程的數據空間中。

在前面,咱們雖然把堆棧和數據空間分開說,那是爲了強調兩個要素在保存數據時的差別。堆棧中的數據隨時變化,例如,進程中的一個函數執行結束,堆棧中的數據就會發生變化。

進程的數據空間呢?我覺得,當進程結束執行的時候,進程的數據空間中的數據纔會消失。這是個人猜想,暫時不知道怎麼去驗證。

認爲堆棧保存在數據空間中的依據是什麼?由於寄存器ss中的選擇子指向的描述符描述的那段內存空間就是數據空間。

進程的ds、es、ss的選擇子相同,指向相同的數據空間。

LDT、GDT和LDT選擇子

每一個進程都有一個LDT。LDT存儲在進程的進程表中。

在進程的進程表中,有一個LDT選擇子。根據LDT選擇子,能從GDT中找到指向LDT的描述符。

有點繞。連起來再說一次:經過進程表中的LDT選擇子,從GDT中找到指向LDT的描述符,根據描述符找到LDT,LDT也在進程表中。

個人收穫

  1. 進程的堆棧存儲在進程的數據空間中。
  2. 堆棧是動態變化的,例如進程中的一個函數執行結束。堆棧中的數據容易消失,因此不能函數的返回值不能是指向堆棧的內存地址。
  3. 在函數中建立字符串變量、結構體變量,數據存儲在進程的數據空間中,存儲在堆棧中的只是數據的內存地址。
  4. 每一個進程的堆棧棧頂能夠是相同的。個人操做系統在初始化進程時,之因此使用不一樣的堆棧棧頂,是由於個人操做系統沒有開啓虛擬內存地址,使用的是相同的內存空間。若是使用相同的堆棧棧頂,不一樣進程的堆棧會相互覆蓋。
  5. fork的實現:
    1. 子進程複製父進程的進程表,可是要使用不一樣的LDT選擇子。
    2. 子進程要複製父進程的數據空間,同時要修改子進程的LDT。
相關文章
相關標籤/搜索