fork函數(shù)的作用
在Linux中fork函數(shù)是非常重要的函數(shù),它的作用是從已經(jīng)存在的進(jìn)程中創(chuàng)建一個(gè)子進(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
這個(gè)簡單的例子有一些微妙的方面:
?調(diào)用一次,返回兩次
fork函數(shù)被父進(jìn)程調(diào)用一次,但是卻返回兩次;一次是返回到父進(jìn)程,一次是返回到新創(chuàng)建的子進(jìn)程。
?并發(fā)執(zhí)行
子進(jìn)程和父進(jìn)程是并發(fā)運(yùn)行的獨(dú)立進(jìn)程。內(nèi)核能夠以任意的方式交替執(zhí)行他們的邏輯控制流中的指令。在我們的系統(tǒng)上運(yùn)行這個(gè)程序時(shí),父進(jìn)程先運(yùn)行它的printf語句,然后是子進(jìn)程。
?相同但是獨(dú)立的地址空間
因?yàn)楦高M(jìn)程和子進(jìn)程是獨(dú)立的進(jìn)程,他們都有自己私有的地址空間,當(dāng)父進(jìn)程或者子進(jìn)程單獨(dú)改變時(shí),不會(huì)影響到彼此,類似于c++的寫實(shí)拷貝的形式自建一個(gè)副本。
?fork的返回值
1.fork的子進(jìn)程返回為0;
2.父進(jìn)程返回的是子進(jìn)程的pid。
?fork的常規(guī)用法
1.一個(gè)父進(jìn)程希望復(fù)制自己,使得子進(jìn)程同時(shí)執(zhí)行不同的代碼段,例如:父進(jìn)程等待客戶端請(qǐng)求,生成一個(gè)子進(jìn)程來等待請(qǐng)求處理。
2.一個(gè)進(jìn)程要執(zhí)行一個(gè)不同的程序。
?fokr調(diào)用失敗的原因
1.系統(tǒng)中有太多進(jìn)程
2.實(shí)際用戶的進(jìn)程數(shù)超過限制
fork函數(shù)創(chuàng)建進(jìn)程
在linux下,C語言創(chuàng)建進(jìn)程用fork函數(shù),接下來我們通過代碼來一步步了解fork函數(shù)的各個(gè)知識(shí)點(diǎn)。
1、依賴的頭文件
1#include 《unistd.h》
2、fork的原理和概念
fork子進(jìn)程就是從父進(jìn)程拷貝一個(gè)新的進(jìn)程出來,子進(jìn)程和父進(jìn)程的進(jìn)程ID不同,但用戶數(shù)據(jù)一樣。
在C語言中,創(chuàng)建一個(gè)子進(jìn)程代碼如下:
pid_t pid; //pid_t 從底層來看,實(shí)際上是int類型。
pid = fork();
3、父進(jìn)程和子進(jìn)程
執(zhí)行fork函數(shù)后有2種返回值:對(duì)于父進(jìn)程,返回的是子進(jìn)程的PID(即返回一個(gè)大于0的數(shù)字);對(duì)于子進(jìn)程,則返回0,所以我們可以通過pid這個(gè)返回值來判斷當(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í)行位置
了解這些之后,我們來看一個(gè)創(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個(gè)“before fork”,但輸出了2個(gè)“after fork”,所以我們可以得出:子進(jìn)程的代碼執(zhí)行是從fork()位置之后開始的。事實(shí)也確實(shí)是如此。
5、循環(huán)創(chuàng)建子進(jìn)程
有時(shí)候,我們需要?jiǎng)?chuàng)建多個(gè)子進(jìn)程,可以通過for循環(huán)來實(shí)現(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個(gè)子進(jìn)程嗎,怎么輸出了這么多次parent process和child process呢?
這是因?yàn)樽舆M(jìn)程也創(chuàng)建了子進(jìn)程,大家可以觀察一下圖中的pid。數(shù)了一下,共輸出了8次,剛好是2的3次方。
我畫了一個(gè)fork步驟圖,便于大家更好的理解,如下:
如上圖所示,子進(jìn)程在第2輪、3輪,也會(huì)相當(dāng)于父進(jìn)程一樣繼續(xù)fork子進(jìn)程,所以for循環(huán)3次后,剛好得到共8個(gè)進(jìn)程。
那如果我們就想通過循環(huán)3次,得到3個(gè)子進(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
評(píng)論
查看更多