在Linux
內核中,無時無刻不維護着進程,從進程的建立到進程銷燬,每個環境都有着複雜的細節。本篇介紹Linux
內核如何建立進程,深刻理解 fork
函數以及子進程的建立,對理解多進程開發也相當重要。編程
首先來看下fork() 函數,其做用是建立子進程。頭文件與函數原型以下多線程
#include <unistd.h> // 參數 : void // 返回值: pid_t 建立的子進程ID pid_t fork(void);
返回值:fork() 返回值會返回兩次,分別在父進程和子進程中返回。函數
fork
的返回值來區分父進程與子進程。-1
,表示建立子進程失敗,並設置 errorno
。以下面兩種狀況致使建立失敗:errno
的值被設置爲EAGAIN
errno
的值被設置爲 ENOMEM
下面建立一個子進程,來講明父進程與子進程的執行順序。線程
#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() 函數,使得函數阻塞,因此父進程與子進程交替執行。3d
經過 fork函數建立進程後,能夠經過返回值判斷是父進程仍是子進程。對於父進程與子進程如何執行,下面介紹fork函數調用後,父子進程如何執行,在進程中虛擬地址空間中如何體現。指針
首先,咱們先看一下上述示例的執行順序,父進程執行執行fork後,返回子進程ID,pid
大於0,因此輸出 if(pid>0)
的內容。
code
子進程在建立成功後,在子進程中返回 0,從當前位置開始執行,因此 pid=0
會輸出 else
語句。
blog
對於虛擬空間地址來講,子進程會拷貝父進程的虛擬地址空間。因此,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函數就介紹到這裏了,本篇介紹了建立子進程的過程,理解父子進程間虛擬地址空間的共享與複製。在多線程開發中,能夠輕鬆分析父子進程的執行順序與內存共享。
點關注,不迷路。一鍵三連是對我最大的支持,歡迎關注編程小鎮,天天漲一點新姿式😄。