【Linux】關於理解fork()函式的簡單例子
1.fork()函式
fork()是一個系統呼叫,用於建立程序。建立的這個程序與原來程序幾乎完全相同。這個新產生的程序稱為子程序。一個程序呼叫fork()函式後,系統先給新的程序分配資源,例如儲存資料和程式碼的空間。然後把原來的程序的所有值都複製到新的新程序中,只有少數值與原來的程序的值不同。相當於克隆了一個自己。需要注意的一點:就是呼叫fork之後,兩個程序同時執行的程式碼段是fork函式之後的程式碼,而之前的程式碼已經由父程序執行完畢。下面來看一個很簡單的例子。
fork()返回兩個值
- 返回一個大於0的值給父程序
- 返回0給子程序
- 返回其他值說明fork失敗了
2.關於fork的一個簡單例子
#include<stdio.h> #include<unistd.h> int main() { pid_t pid; int count = 0; pid = fork(); //fork一個程序 if(pid == 0) { //pid為0, printf("this is child process, pid is %d\n",getpid());//getpid返回的是當前程序的PID count+=2; printf("count = %d\n",count); } else if(pid > 0) { printf("this is father process, pid is %d\n",getpid()); count++; printf("count = %d\n",count); } else { fprintf(stderr,"ERROR:fork() failed!\n"); } return 0; }
下面是執行結果:
有人可能會對這個執行結果產生一種錯覺,就是程式中if語句的兩條分支if(pid == 0)和else if(pid > 0)都得到了執行。其實完全不是這麼回事,出現這種執行結果的原因是因為,在main()函式呼叫fork了,建立了一個新的程序,這個程序稱為原來程序的子程序。子程序與原來的程序併發執行,誰先誰後沒有規律,由作業系統排程決定。
我們用gdb工具對這個程式單步執行除錯一下,可能會對這個過程瞭解的更清晰一些。
3.使用gdb除錯父程序。
1.輸入gdb a.out然後輸入start開始除錯,此時終端顯示即將執行的程式碼
2.輸入n,執行當前一行程式碼,同時顯示下一條將執行的程式碼
即將建立一個子程序,當前程序為父程序。
3.再輸入n執行pid=fork()語句
這個時候,終端上顯示
Detaching after fork from child process 2755.
this is child process, pid is 2755
count = 2
這個時候,說明已經新建了一個子程序,子程序PID是2755,而且由於我們現在單步除錯的是父程序,並不影響子程序的執行。這個子程序只有幾行程式碼,這個時候已經執行完了,並且在終端上顯示了執行結果,就是下面兩行內容。
this is child process, pid is 2755
count = 2
4. 輸入p pid看一下fork函式返回給父程序的值是不是子程序的PID
果然就是子程序的PID值。
5.輸入n接著執行判斷語句
由於pid的值為2755,因此跳過了if(pid==0)分支內的語句,轉而判斷pid是否大於0
6.輸入n接著判斷pid是否大於0
pid的值大於0,執行分支內的語句。
7.接著輸入n,直到程式正常結束
上面是父程序的除錯過程,其實子程序也可以用gdb來除錯,下次再說。
4.使用ps aux命令檢視父程序和子程序
ps aux命令可以檢視系統中正在執行的所有程序,不過我們這個例子程式碼很少,系統瞬間就能執行完畢,用ps aux命令根本捕捉不到。因此我們在程式碼中做一些修改。
#include<stdio.h>
#include<unistd.h>
int main()
{
pid_t pid;
int count = 0;
pid = fork(); //fork一個程序
if(pid == 0) { //pid為0,
printf("this is child process, pid is %d\n",getpid());
count+=2;
printf("count = %d\n",count);
} else if(pid > 0) {
printf("this is father process, pid is %d\n",getpid());
count++;
printf("count = %d\n",count);
} else {
fprintf(stderr,"ERROR:fork() failed!\n");
}
sleep(10);//新加入的行,讓程式在這裡暫停10秒,父程序和子程序都會執行這行程式碼
return 0;
}
我們在第30行加入了一條語句,讓程式暫停10s。重新編譯程式,然後執行程式,輸入./a.out後,快速切換到另外一個終端(如果你手速慢的話,多暫停一會兒就可以),輸入ps aux檢視正在執行的程序。
執行程式:
切換終端,輸入ps aux檢視程序
可以看到a.out程式產生了兩個程序,父程序PID是2928,子程序PID是2929。