一、進程的建立
1.系統調用clone()與fork()的區別:
| |
資源的繼承方式 |
參數 |
區分父進程與子進程的方法 |
| fork() |
全部複製,即父進程的所有資源全部通過資料結構的複製傳給子進程 |
無參數 |
父進程與子進程,從fork()返回時的傳回值不同,以此區分二者。子進程返回0.父進程返回子進程的pid |
| clone() |
有選擇地將資源複製給子進程,沒有複製的資源通過指標複製的方式與子進程共用(共用與複製的關係見2) |
有參數,用於設定資源複製的方式 |
clone()所產生的子線程的PID有可能是0,因此採用比較系統堆棧指標的方法來區分二者。 |
note:
(1)為什麼傳回值的方式對fork()適用?
因為fork()後子進程擁有獨立的資源,是一個進程,有自己的獨一無二的PID,不可能是0
(2)為什麼返回的方式對clone()不適用?
因為clone()不一定複製資源,產生的有可能是一個線程。線程的PID與它所在的線程組的PID相同。如果父線程的PID是0,那麼子線程的PID也是0。父與子的傳回值是一樣的,無法區分。
(3)為什麼比較系統堆棧指標的方式對fork()不適用?
因為普通的進程都在使用者空間,系統堆棧不知道在哪裡
(4)為什麼比較系統堆棧指標的方式對clone()適用?
每一個核心線程都有自己的系統空間堆棧,子線程的堆棧空間的指標必須與父線程不同
(5)它們最終都是調用do_fork()建立子進程的
note中的內容講得不好理解,因為有幾句話強調線程與進程的區別,有幾句話裡進程指的是進程或線程,如果結合Linux2.6進程-1中的表格,會有助於對note的理解
2.線程建立函數kernel_thread(),本質也是調用do_fork(),但是不複製進程的頁表。因為新核心線程不會訪問使用者態地址空間
3.共用與複製的關係
(1)相同點:
複製完全之初,子進程有了一個副本,它的內容與父進程的正本內容基本相同。
(2)不同點-共用:
共用是指僅複製指標,是淺層複製。事實上,父與子都是對同一地址進程操作。
父與子並發地對同一堆棧區進行操作,這種行為是致命的。
通常的解決方案是:扣留父進程,只讓子進程返回。當子進程有了自己的使用者空間,或子進程確定不再使用父的使用者空間(死亡)後,才能讓父返回。
(3)不同點-複製:
互不干擾
4.所有用於建立子進程的系統調用,最終都是通過調用do_fork()建立子進程的
(1)擷取一個空進程項
(2)擷取一頁記憶體
(3)自製當前進程的資料結構到記憶體中
(4)新進程設定為不可中斷等待
(5)對新進程的資料結構修改
note:不分配實體記憶體,寫時複製
5.寫時複製
(1)將父進程的頁面表項改為防寫保護
(2)把表項設定到子進程的頁面表中
(3)當父進程或子進程企圖寫頁面時,發生頁面異常,給要寫的進程另分配一個物理頁面,此時才是真正的複製
(5)置兩個頁面都為可寫
6.父進程建立子進程後,父進程剩餘節拍數被劃分為兩等份,一份給父,一份給子。
一個進程不能通過建立多個後代來霸佔資源
二、進程的撤消與刪除
1.子進程被終止以後,不能立即刪除,因為它可能還有父進程需要的資訊。
只有父進程發出了與被終止進程相關的wait類系統調用後,才允許由父進程為子進程徹底刪除
2.子進程在do_exit()的最後,會調用schedule()。這個函數的返回條件是再次被調度。但此進程再也不會被調度,因此schedule()不會返回,do_exit()也不會被返回
此時進程的狀態是ZOMBIE,即調度被無限延遲,直到父進程收到訊號後來處理後事,將其task_struct釋放,這個進程就徹底消失了。
3.父進程要為子進程處理後事。
如果一個進程被終止前還有子進程,則要把子進程託付給某個進程。
如果父進程是一個線程組的成員,就把子進程託付給線程組的下一個成員。
否則就把子進程託付給0號進程
4.中斷服務程式、軟體中斷服務程式、0號進程、1號進程不允許終止。