當進程調用fork後,當控制轉移到內核中的fork代碼後,內核會做4件事情:
1.分配新的內存塊和內核數據結構給子進程
2.將父進程部分數據結構內容(數據空間,堆棧等)拷貝至子進程
3.添加子進程到系統進程列表當中
4.fork返回,開始調度器調度
fork()函數的底層實現:
Linux通過clone()系統調用實現fork()。這個調用通過一系列的參數標誌來指明父、子進程需要共享的資源。fork()、vfork()和_clone()庫函數都根據各自需要的參數標誌去調用clone()。然後由clone()去調用do_fork()。do_fork完成了創建中的大部分工作,它的定義在kernel/fork.c文件中。該函數調用copy_process()函數,然後讓進程開始運行。copy_process()函數做的具體事情:
調用dup_task_struct()爲新進程創建一個內核棧、thread_info結構和task_stuct,這些值與當前進程的值相同。此時,子進程和父進程的描述符是完全相同的。
檢查新創建的這個子進程後,當前用戶所擁有的進程數目沒有超出給他分配的資源的限制。
現在,子進程着手使自已與父進程區別開來。進程描述符內的許多成員都要被清0或設爲初始值。進程描述符的成員值並不是繼承而來的,而主要是統計信息。進程描述符中的大多數數據都是共享的。
接下來,子進程的狀態被設置爲TASK_UNINTERRUPTIBLE以保證它不會投入運行。
copy_process()調用copy_flags()以更新task_struct的flags成員。表明進程是否擁有超級用戶權限的PF_SUPERPRIV標誌被清0。表明進程還沒有調用excc()函數的PF_FORKNOEXEC標誌被設置。
調用get_pid()爲新進程獲取一個有效的PID。
根據傳遞給clone()的參數標誌,copy_process()拷貝或共享打開的文件、文件系統信息、信號處理函數、進程地址空間和命名空間等。在一般情況下,這些資源會被給定進程的所有線程共享;否則,這些資源對每個進程是不同的,因此被拷貝到這裏。
讓父進程和子進程平分剩餘的時間片。最後,copy_process()作掃尾工作並返回一個指向子進程的指針。
再回到do_fork()函數,如果copy_pocess函數成功返回,新創建的子進程被喚醒並讓其投入運行。內核有意選擇子進程首先執行。因爲一般子進程都會馬上調用excc()函數,這樣可以避免寫時拷貝的額外開銷,如果父進程首先執行的話,有可能會開始向地址空間寫入。