??在Linux 內核中,無時無刻不維護著行程,從行程的創建到行程銷毀,每一個環境都有著復雜的細節,本篇介紹Linux 內核如何創建行程,深入理解 fork 函式以及子行程的創建,對理解多行程開發也至關重要,
- fork() 函式
- fork() 示例
- 父子行程的虛擬地址空間
fork() 函式
??首先來看下fork() 函式,其作用是創建子行程,頭檔案與函式原型如下
#include <unistd.h>
// 引數 : void
// 回傳值: pid_t 創建的子行程ID
pid_t fork(void);
??回傳值:fork() 回傳值會回傳兩次,分別在父行程和子行程中回傳,
- 在父行程中回傳子行程的ID,在子行程中回傳0,所以可以通過
fork的回傳值來區分父行程與子行程, - 在父行程中回傳
-1,表示創建子行程失敗,并設定errorno,如下面兩種情況導致創建失敗:
- 當前系統的行程數已經達到了系統規定的上限,這時
errno的值被設定為EAGAIN - 系統記憶體不足,這時
errno的值被設定為ENOMEM
fork() 示例
下面創建一個子行程,來說明父行程與子行程的執行順序,
#include <unistd.h>
#include <stdio.h>
int main(){
// 創建行程
pid_t pid = fork();
// 判斷當前行程是父行程 還是子行程
if (pid > 0){ // 行程號 > 0,即為子行程的行程號,當前為父行程
printf("pid: %d\n", pid);
printf("I am parent process, pid: %d, ppid: %d\n", getpid(), getppid());
}
else if (pid == 0){ // 行程號 == 0,表示當前為子行程
printf("I am child process, pid: %d, ppid: %d\n", getpid(), getppid());
}
for (int i = 0; i < 5; i++){
printf("pid: %d, i : %d\n", getpid(), i);
sleep(1);
}
return 0;
}
??編譯執行,可以看到,子行程創建成功,兩個行程同時執行,父行程ID 為412552,子行程ID為512553,由于sleep() 函式,使得函式阻塞,所以父行程與子行程交替執行,

父子行程的虛擬地址空間
??通過 fork函式創建行程后,可以通過回傳值判斷是父行程還是子行程,對于父行程與子行程如何執行,下面介紹fork函式呼叫后,父子行程如何執行,在行程中虛擬地址空間中如何體現,
??
??首先,我們先看一下上述示例的執行順序,父行程執行執行fork后,回傳子行程ID,pid 大于0,所以輸出 if(pid>0) 的內容,

??子行程在創建成功后,在子行程中回傳 0,從當前位置開始執行,所以 pid=0 會輸出 else 陳述句,

?
??對于虛擬空間地址來說,子行程會拷貝父行程的虛擬地址空間,所以,fork后子行程的用戶區與父行程的用戶區相同,也會拷貝內核區內容,僅僅是行程的 pid不同,

??對于父行程中的堆疊空間的變數,也會原封不動的拷貝至子行程的堆疊空間中,但這兩個變數互不影響,父行程改變變數不會影響子行程變數,看如下程式展示父子行程中堆疊空間變數的使用,
#include <unistd.h>
#include <stdio.h>
int main(){
// 創建行程
pid_t pid = fork();
// 區域變數
int num = 10;
// 判斷當前行程是父行程 還是子行程
if (pid > 0){ // 行程號 > 0,即為子行程的行程號,當前為父行程
printf("I am parent process, pid: %d, ppid: %d\n", getpid(), getppid());
printf("parent process num : %d\n", num);
num += 10;
printf("parent process num + 10 : %d\n", num);
}
else if (pid == 0){ // 行程號 == 0,表示當前為子行程
printf("I am child process, pid: %d, ppid: %d\n", getpid(), getppid());
printf("child process num : %d\n", num);
num += 100;
printf("child process num + 100 : %d\n", num);
}
return 0;
}
編譯執行,可以看到,父行程中的區域變數num 與子行程的區域變數互不影響,

??
讀時共享,寫時拷貝技術
??實際上,準確的來說,Linux的fork 是通過 寫時拷貝 (copy-on-write)實作,寫時拷貝是一種可以推遲甚至不用避免拷貝的技術,更具體來講,在執行fork陳述句后,內核并不復制父行程的整個地址空間,而是父子行程共享父行程的地址空間(此時父子行程對于地址空間是只讀指令),在父行程或者子行程進行寫指令時,子行程才會復制一份地址空間,從而使得父子行程擁有自己的虛擬地址空間,在自己的地址空間進行寫操作,也就是說,資源的復制是在需要寫入時才會進行,在此之前,只會以只讀方式進行共享,
?
??對于檔案資源,fork之后的父子行程共享檔案,fork之后的父行程與子行程的檔案描述符表指向相同的檔案表,參考計數增加,共享檔案偏移指標,
??fork函式就介紹到這里了,本篇介紹了創建子行程的程序,理解父子行程間虛擬地址空間的共享與復制,在多執行緒開發中,可以輕松分析父子行程的執行順序與記憶體共享,
?
?
點關注,不迷路,一鍵三連是對我最大的支持,歡迎關注編程小鎮,每天漲一點新姿勢??,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/243450.html
標籤:C++
上一篇:C++學習筆記1
