fork函數(shù)的作用
在Linux中fork函數(shù)是非常重要的函數(shù),它的作用是從已經(jīng)存在的進(jìn)程中創(chuàng)建一個子進(jìn)程,而原進(jìn)程稱為父進(jìn)程。
調(diào)用fork(),當(dāng)控制轉(zhuǎn)移到內(nèi)核中的fork代碼后,內(nèi)核開始做:
1.分配新的內(nèi)存塊和內(nèi)核數(shù)據(jù)結(jié)構(gòu)給子進(jìn)程。
2.將父進(jìn)程部分?jǐn)?shù)據(jù)結(jié)構(gòu)內(nèi)容拷貝至子進(jìn)程。
3.將子進(jìn)程添加到系統(tǒng)進(jìn)程列表。
4.fork返回開始調(diào)度器,調(diào)度。
來段代碼:
1 #include《stdio.h》
2 #include《unistd.h》
3 #include《stdlib.h》
4 int main()
5 {
6 pid_t pid;
7 printf(“before :pid is %d ”,getpid());
8 if((pid=fork())==-1)
9 perror(“fork()”),exit(1);
10 printf(“After:pid=%d,fork return %d ”,getpid(),pid);
11 sleep(1);
12
13 return 0;
14 }
15 123456789101112131415
這個簡單的例子有一些微妙的方面:
?調(diào)用一次,返回兩次
fork函數(shù)被父進(jìn)程調(diào)用一次,但是卻返回兩次;一次是返回到父進(jìn)程,一次是返回到新創(chuàng)建的子進(jìn)程。
?并發(fā)執(zhí)行
子進(jìn)程和父進(jìn)程是并發(fā)運(yùn)行的獨立進(jìn)程。內(nèi)核能夠以任意的方式交替執(zhí)行他們的邏輯控制流中的指令。在我們的系統(tǒng)上運(yùn)行這個程序時,父進(jìn)程先運(yùn)行它的printf語句,然后是子進(jìn)程。
?相同但是獨立的地址空間
因為父進(jìn)程和子進(jìn)程是獨立的進(jìn)程,他們都有自己私有的地址空間,當(dāng)父進(jìn)程或者子進(jìn)程單獨改變時,不會影響到彼此,類似于c++的寫實拷貝的形式自建一個副本。
?fork的返回值
1.fork的子進(jìn)程返回為0;
2.父進(jìn)程返回的是子進(jìn)程的pid。
?fork的常規(guī)用法
1.一個父進(jìn)程希望復(fù)制自己,使得子進(jìn)程同時執(zhí)行不同的代碼段,例如:父進(jìn)程等待客戶端請求,生成一個子進(jìn)程來等待請求處理。
2.一個進(jìn)程要執(zhí)行一個不同的程序。
?fokr調(diào)用失敗的原因
1.系統(tǒng)中有太多進(jìn)程
2.實際用戶的進(jìn)程數(shù)超過限制
fork函數(shù)創(chuàng)建進(jìn)程
在linux下,C語言創(chuàng)建進(jìn)程用fork函數(shù),接下來我們通過代碼來一步步了解fork函數(shù)的各個知識點。
1、依賴的頭文件
1#include 《unistd.h》
2、fork的原理和概念
fork子進(jìn)程就是從父進(jìn)程拷貝一個新的進(jìn)程出來,子進(jìn)程和父進(jìn)程的進(jìn)程ID不同,但用戶數(shù)據(jù)一樣。
在C語言中,創(chuàng)建一個子進(jìn)程代碼如下:
pid_t pid; //pid_t 從底層來看,實際上是int類型。
pid = fork();
3、父進(jìn)程和子進(jìn)程
執(zhí)行fork函數(shù)后有2種返回值:對于父進(jìn)程,返回的是子進(jìn)程的PID(即返回一個大于0的數(shù)字);對于子進(jìn)程,則返回0,所以我們可以通過pid這個返回值來判斷當(dāng)前進(jìn)程是父進(jìn)程還是子進(jìn)程。如下代碼所示:
if(pid 》 0)
{
printf(“im parent process, pid: %d ”, getpid());
}
else if(pid == 0)
{
printf(“im child process, pid: %d, parent pid: %d ”, getpid(), getppid());
}
else
{
printf(“fork failed ”);
}
溫馨提示:
getpid() -獲取當(dāng)前進(jìn)程的pid
getppid() -獲取當(dāng)前進(jìn)程的父進(jìn)程的pid
4、完整例子&子進(jìn)程代碼執(zhí)行位置
了解這些之后,我們來看一個創(chuàng)建子進(jìn)程的完整代碼示例:
#include 《stdio.h》
#include 《unistd.h》
int main(int argc, char *argv[])
{
printf(“========== before fork ============= ”);
pid_t pid;
pid = fork();
printf(“========== after fork ============= ”);
if(pid 》 0)
{
printf(“im parent process, pid: %d ”, getpid());
}
else if(pid == 0)
{
printf(“im child process, pid: %d, parent pid: %d ”, getpid(), getppid());
}
else
{
printf(“fork failed ”);
}
printf(“========== process end ============= ”);
sleep(1);
return 0;
}
運(yùn)行結(jié)果如下圖:
從上圖可以看出,程序只輸出了1個“before fork”,但輸出了2個“after fork”,所以我們可以得出:子進(jìn)程的代碼執(zhí)行是從fork()位置之后開始的。事實也確實是如此。
5、循環(huán)創(chuàng)建子進(jìn)程
有時候,我們需要創(chuàng)建多個子進(jìn)程,可以通過for循環(huán)來實現(xiàn),代碼如下:
#include 《stdio.h》
#include 《unistd.h》
int main(int argc, char *argv[])
{
int i = 0;
pid_t pid;
for(i = 0; i 《 3; i++)
{
pid = fork();
}
if (pid == 0)
{
printf(“im child process, pid: %d, parent pid: %d ”, getpid(), getppid());
}
else
{
printf(“im parent process, pid: %d ”, getpid());
}
sleep(1);
return 0;
}
運(yùn)行結(jié)果如下圖:
咦,我們不是循環(huán)創(chuàng)建3個子進(jìn)程嗎,怎么輸出了這么多次parent process和child process呢?
這是因為子進(jìn)程也創(chuàng)建了子進(jìn)程,大家可以觀察一下圖中的pid。數(shù)了一下,共輸出了8次,剛好是2的3次方。
我畫了一個fork步驟圖,便于大家更好的理解,如下:
如上圖所示,子進(jìn)程在第2輪、3輪,也會相當(dāng)于父進(jìn)程一樣繼續(xù)fork子進(jìn)程,所以for循環(huán)3次后,剛好得到共8個進(jìn)程。
那如果我們就想通過循環(huán)3次,得到3個子進(jìn)程,要怎么辦呢?
思路:不讓子進(jìn)程fork出新的子進(jìn)程。
代碼片段如下:
for(i = 0; i 《 3; i++)
{
pid = fork();
if (pid == 0)
{
break;
}
}
運(yùn)行結(jié)果:
至此,fork函數(shù)創(chuàng)建子進(jìn)程介紹完畢。
責(zé)任編輯:YYX
評論