1. 程式人生 > >二、父子程序的關係

二、父子程序的關係

一、如何在程序中建立一個新的程序(fork函式)
1、pid_t fork(void);
(1)fork函式與別的函式有所不同,它呼叫一次,返回兩次。在新創建出來的程序中返回一次(返回值為 0),在原來的程序中返回一次(返回值為新程序的pid)。
(2)fork函式呼叫完成以後,兩個程序都從fork之後開始執行。
(3)fork函式複製出新的程序後,兩個程序成為獨立的個體,互相不影響,各自執行。並且哪個程序先執 行不一定。

2、先了解一下父子程序的定義:
通過fork函式建立的新程序是呼叫fork函式程序的子程序,而呼叫foek函式的程序為新程序的父程序。即就是,通過fork建立的程序與原程序之間時父子關係。
3、再瞭解兩個函式:
getpid():使用者獲取呼叫getpid這個函式的程序的pid;
getppid():使用者獲取呼叫getppid這個函式的程序的父程序的pid;
4、孤兒程序:
當父程序結束以後,子程序未結束,子程序就是孤兒程序。這是子程序的父程序就變成init。
 init這個程序是守護程序,也叫孤兒院,負責處理這些孤兒程序推出以後的一些狀態。
5、僵死程序(殭屍程序):父程序未結束,而子程序結束,父程序未獲取子程序的推出資料,這時,子程序為殭屍程序。

6、fork之後,父子程序之間的資料是否共享呢?
全域性資料、棧區資料、堆區資料是不共享的;
而父子程序對於檔案描述符共享,並且共享檔案偏移量。

pid_t fork(void); //父程序返回子程序的pid,子程序返回0,錯誤返回-1

fork()建立了一個新的程序(child),新程序幾乎是呼叫程序(父程序)的翻版,理解fork()的關鍵是,在完成對其呼叫之後,會產生2個程序,且每個程序都會從fork()的返回處開始執行.

這倆個程序將執行相同的程式段,但是擁有各自不同的堆段,棧段,資料段,每個子程式都可修改各自的資料段,堆段,和棧段

父子程序的程式碼段是相同的,所以程式碼段是沒必要複製的,因此核心將程式碼段標記為只讀,這樣父子程序就可以安全的共享此程式碼段了。fork之後在程序建立程式碼段時,新子程序的程序級頁表項都指向和父程序相同的物理頁幀


(2)而對於父程序的資料段,堆段,棧段中的各頁,由於父子程序要相互獨立,所以我們採用寫實複製的技術,來最大化的提高記憶體以及核心的利用率。剛開始,核心做了一些設定,令這些段的頁表項指向父程序相同的實體記憶體頁。呼叫fork之後,核心會捕獲所有父程序或子程序針對這些頁面的修改企圖(說明此時還沒修改)併為將要修改的頁面建立拷貝。系統將新的頁面拷貝分配給被核心捕獲的程序,還會對子程序的相應頁表項做適當的調整,現在父子程序就可以分別修改各自的上述段,不再互相影響了
寫實複製前: 


寫實賦值後: 

二、資料共享

我們現在通過一段程式碼研究一下父子程序的是否存在資料共享。

  我們現在簡單把資料分成三種:堆區資料、棧區資料、全域性資料

。所以我現在有三個變數,同時,我讓子程序改變這三個變數的值。讓父程序沉睡兩秒鐘以確保子程序執行結束。

  結果如下:

  可以看到,其值發生了改變,這也說明,

      父子程序的堆區資料、棧區資料、全域性資料是不共享的。

  那子程序能和父程序共享檔案嗎?我們看看以下程式碼:

  我先open一個我建立好的在同一目錄下的a.txt檔案,a.txt檔案中我寫了“Hello World!”

  讓父子程序一起一個位元組一個位元組的讀檔案裡字元,每讀一次sleep一秒鐘。

  結果如下:

  這是什麼呢,這就是說,並不是一個程序操作一個檔案時,另一個程序就不能操作,而且,父子程序可以同時操作同一個檔案。

  也就是,

       在父子程序中,檔案描述符以及檔案偏移量,都是共享的!

   為什麼檔案就能共享了呢,在linux原始碼裡,每個程序都有一個PCB(程序控制塊)結構體,每個PCB裡,存了一個結構體指標指向一個我們理解為檔案描述符的結構體struct file,而這個結構體裡,才存了檔案的id,從什麼地方開始寫,模式等等。值得注意的事,這個結構體裡有一個指標才是指向真正的檔案的。