程序建立之fork函數
頭檔案:#include<unistd.h>
#include <sys/types.h>
函數原型:pid_t fork(void); pid_t實際就是int,在sys/types.h中被宏定義的
傳回值:1.建立程序失敗---傳回-1(失敗原因有兩個:1)目前的程序數已經達到了系統規定的上限,這時errno的值被設定為EAGAIN。2)系統記憶體不足,這時errno的值被設定為ENOMEM)
2.建立成功:子程序傳回0,父程序傳回子程序ID
函數說明:一個現有的程序調用fork函數建立一個新的程序,新程序稱之為子程序,調用fork函數的程序稱為父程序。子程序是父程序的副本,它将獲得父程序資料空間、堆棧等資源的副本。子程序持有的是上述存儲空間的“副本”,父子程序間不共享這些存儲空間。linux将複制父程序的位址空間内容給子程序,是以,子程序有了獨立的位址空間。在Linux下,一個程序在記憶體裡有三部分的資料---"代碼段"、"堆棧段"和"資料段"。"代碼段",存放程式代碼的資料,假如機器中有數個程序運作相同的一個程式,那麼它們就可以使用相同的代碼段。"堆棧段"存放的是子程式的傳回位址、子程式的參數以及程式的局部變量等。“資料段”則存放程式的全局變量,常數以及動态資料配置設定的資料空間(比如用malloc之類的函數取得的空間)。調用fork函數,父子程序共用代碼段,但是資料段和堆棧段是獨立的。也就是說資料段和堆棧段的資料是互相不影響的
示例代碼:
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
static int s_nTmp1 = 0;
static int s_nTmp2 = 0;
int main(void)
{
int nTmp = 0;
pid_t pid = 0;
printf("Before fork\n");
pid = fork();
printf("This is a test\n");
if(pid > 0)
{
nTmp = 1;
s_nTmp1 = 1;
s_nTmp2 = 1;
printf("這是父程序,它的pid=%d,他的子程序的pid=%d\n", getpid(), pid);
printf("父程序--nTmp = %d\n", nTmp);
printf("父程序--s_nTmp1 = %d\n", s_nTmp1);
printf("父程序--s_nTmp2 = %d\n", s_nTmp2);
while(1);
}
else
{
printf("這是子程序,它的pid=%d,它的父程序的pid=%d\n", getpid(), getppid());
printf("子程序--nTmp = %d\n", nTmp);
printf("子程序--s_nTmp1 = %d\n", s_nTmp1);
printf("子程序--s_nTmp2 = %d\n", s_nTmp2);
while(1);
}
return 0;
}
執行結果:
Before fork
This is a test
這是父程序,它的pid=2515,他的子程序的pid=2516
父程序--nTmp = 1
父程序--s_nTmp1 = 1
父程序--s_nTmp2 = 1
This is a test
這是子程序,它的pid=2516,它的父程序的pid=2515
子程序--nTmp = 0
子程序--s_nTmp1 = 0
子程序--s_nTmp2 = 0
結果分析:執行結果中可以看到,在fork之前的代碼隻會被執行一次(Before fork隻會被列印一次),而fork之後的代碼都會被執行兩次(This is a test列印了兩次),這是因為父程序和子程序共享代碼段,各執行了一次fork之後的代碼。在fork之後,父程序和子程序都有自己獨立資料段和堆棧段(子程序的資料段和堆棧段都是父程序的拷貝),是以,在任意程序中(無論是父程序還是子程序)對全局變量(資料段)和局部變量(堆棧段)的操作都不會影響到另一個程序中的值。
注意點:
1)執行結果也可能是
Before fork
This is a test
這是子程序,它的pid=2516,它的父程序的pid=2515
子程序--nTmp = 0
子程序--s_nTmp1 = 0
子程序--s_nTmp2 = 0
This is a test
這是父程序,它的pid=2515,他的子程序的pid=2516
父程序--nTmp = 1
父程序--s_nTmp1 = 1
父程序--s_nTmp2 = 1
即先執行父程序還是子程序是未知的,這點千萬要注意。(無論哪個先執行,fork之前的代碼肯定都在之前)
2)在程序的結束時,也可以不加while(1),此時會退出一個程序,然後又繼續執行另一個程序,即
Before fork
This is a test
這是父程序,它的pid=2635,他的子程序的pid=2636
父程序--nTmp = 1
父程序--s_nTmp1 = 1
父程序--s_nTmp2 = 1
[email protected]:~/home/progress$ This is a test
這是子程序,它的pid=2636,它的父程序的pid=1
子程序--nTmp = 0
子程序--s_nTmp1 = 0
子程序--s_nTmp2 = 0
此時父程序先退出,然後子程序繼續執行
程序建立之vfork函數
頭檔案:同fork函數
函數原型:pid_t vfork(void); pid_t實際就是int,在sys/types.h中被宏定義的
傳回值:同fork函數
函數說明:vfork函數和fork函數的作用都是建立一個新的程序,但是他們有以下幾點不同
1)vfork函數---父子程序共享代碼段和以及資料空間(資料段和堆棧段);fork函數---父子程序共用代碼段,但是資料段和堆棧段是獨立的。
2)vfork函數---保證子程序先運作,隻有子程序調用exit等函數退出程序時,父程序才能運作,否則阻塞;fork函數---不保證父子程序哪個先執行
示例代碼:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
static int s_nTmp1 = 0;
static int s_nTmp2 = 0;
int main(void)
{
int nTmp = 0;
pid_t pid = 0;
printf("Before fork\n");
pid = vfork();
printf("This is a test\n");
if(pid > 0)
{
printf("這是父程序,它的pid=%d,他的子程序的pid=%d\n", getpid(), pid);
printf("父程序--nTmp = %d\n", nTmp);
printf("父程序--s_nTmp1 = %d\n", s_nTmp1);
printf("父程序--s_nTmp2 = %d\n", s_nTmp2);
}
else
{
nTmp = 1;
s_nTmp1 = 1;
s_nTmp2 = 1;
printf("這是子程序,它的pid=%d,它的父程序的pid=%d\n", getpid(), getppid());
printf("子程序--nTmp = %d\n", nTmp);
printf("子程序--s_nTmp1 = %d\n", s_nTmp1);
printf("子程序--s_nTmp2 = %d\n", s_nTmp2);
sleep(10);
exit(0);
}
return 0;
}
執行結果:
Before fork
This is a test
這是子程序,它的pid=2234,它的父程序的pid=2233
子程序--nTmp = 1
子程序--s_nTmp1 = 1
子程序--s_nTmp2 = 1
This is a test
這是父程序,它的pid=2233,他的子程序的pid=2234
父程序--nTmp = 1
父程序--s_nTmp1 = 1
父程序--s_nTmp2 = 1
結果分析:
調用vfork函數肯定是先執行子程序的代碼,隻有子程序退出後,父程序才得以運作,是以在執行完
printf("子程序--s_nTmp2 = %d\n", s_nTmp2);後,子程序休眠10s,等待10s之後子程序退出,此時才執行父程序代碼。
執行結果隻有上面這一種。在子程序中改變了資料段和堆棧段的資料,都會影響父程序。
注意點:調用vfork函數建立子程序,要防止互鎖導緻程式無法繼續往下執行。比如
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
int nTmp = 0;
pid_t pid = 0;
printf("Before fork\n");
pid = vfork();
printf("This is a test\n");
if(pid > 0)
{
nTmp = 1;
}
else
{
while(1)
{
if(1 == nTmp)
{
printf("執行子程序\n");
exit(0);
}
}
}
return 0;
}
這段代碼在列印完This is a test之後,将阻塞,子程序一直等待nTmp等1時才會退出,而nTmp隻在父程序中被指派為1,父程序隻有在子程序結束時才能執行,導緻了父子程序之間互鎖。
初學者筆記,若有錯誤,請指出,謝謝!