fork、父進程和子進程


進程

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

這是標准的進程概念。讓我們通過操作系統的fork函數看看這個抽象的概念是怎么在進程的實現中體現出來的。

構成要素

創建一個進程,需要進程體、進程表和數據空間。

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

進程表用來記錄進程的進程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不是存儲在進程的堆棧中呢?

函數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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM