fork()使用(一)
作者:ccf ??發(fā)表于:2006-04-01 17:11:01
#include <unistd.h>; #include <sys/types.h>;
main ()
{
???? pid_t pid;
????????? pid=fork();
????????? if (pid < 0)
????????????????? printf("error in fork!");
????????? else if (pid == 0)
????????????????? printf("i am the child process, my process id is %dn",getpid());
????????? else
????????????????? printf("i am the parent process, my process id is %dn",getpid());
}
結(jié)果是
[root@localhost c]# ./a.out
i am the child process, my process id is 4286
i am the parent process, my process id is 4285
我就想不到為什么兩行都打印出來了,在我想來,不管pid是多少,都應(yīng)該只有一行才對
chg.s 回復(fù)于:2004-04-27 21:09:30
要搞清楚fork的執(zhí)行過程,就必須先講清楚操作系統(tǒng)中的“進程(process)”概念。一個進程,主要包含三個元素:
o. 一個可以執(zhí)行的程序;
o. 和該進程相關(guān)聯(lián)的全部數(shù)據(jù)(包括變量,內(nèi)存空間,緩沖區(qū)等等);
o. 程序的執(zhí)行上下文(execution context)。
不妨簡單理解為,一個進程表示的,就是一個可執(zhí)行程序的一次執(zhí)行過程中的一個狀態(tài)。操作系統(tǒng)對進程的管理,典型的情況,是通過進程表完成的。進程表中的每一個表項,記錄的是當(dāng)前操作系統(tǒng)中一個進程的情況。對于單 CPU的情況而言,每一特定時刻只有一個進程占用 CPU,但是系統(tǒng)中可能同時存在多個活動的(等待執(zhí)行或繼續(xù)執(zhí)行的)進程。
一個稱為“程序計數(shù)器(program counter, pc)”的寄存器,指出當(dāng)前占用 CPU的進程要執(zhí)行的下一條指令的位置。
當(dāng)分給某個進程的 CPU時間已經(jīng)用完,操作系統(tǒng)將該進程相關(guān)的寄存器的值,保存到該進程在進程表中對應(yīng)的表項里面;把將要接替這個進程占用 CPU的那個進程的上下文,從進程表中讀出,并更新相應(yīng)的寄存器(這個過程稱為“上下文交換(process context switch)”,實際的上下文交換需要涉及到更多的數(shù)據(jù),那和fork無關(guān),不再多說,主要要記住程序寄存器pc指出程序當(dāng)前已經(jīng)執(zhí)行到哪里,是進程上下文的重要內(nèi)容,換出 CPU的進程要保存這個寄存器的值,換入CPU的進程,也要根據(jù)進程表中保存的本進程執(zhí)行上下文信息,更新這個寄存器)。
好了,有這些概念打底,可以說fork了。當(dāng)你的程序執(zhí)行到下面的語句:pid=fork();??
操作系統(tǒng)創(chuàng)建一個新的進程(子進程),并且在進程表中相應(yīng)為它建立一個新的表項。新進程和原有進程的可執(zhí)行程序是同一個程序;上下文和數(shù)據(jù),絕大部分就是原進程(父進程)的拷貝,但它們是兩個相互獨立的進程!此時程序寄存器pc,在父、子進程的上下文中都聲稱,這個進程目前執(zhí)行到fork調(diào)用即將返回(此時子進程不占有CPU,子進程的pc不是真正保存在寄存器中,而是作為進程上下文保存在進程表中的對應(yīng)表項內(nèi))。問題是怎么返回,在父子進程中就分道揚鑣。
父進程繼續(xù)執(zhí)行,操作系統(tǒng)對fork的實現(xiàn),使這個調(diào)用在父進程中返回剛剛創(chuàng)建的子進程的pid(一個正整數(shù)),所以下面的if語句中pid<0, pid==0的兩個分支都不會執(zhí)行。所以輸出i am the parent process...
子進程在之后的某個時候得到調(diào)度,它的上下文被換入,占據(jù) CPU,操作系統(tǒng)對fork的實現(xiàn),使得子進程中fork調(diào)用返回0。所以在這個進程(注意這不是父進程了哦,雖然是同一個程序,但是這是同一個程序的另外一次執(zhí)行,在操作系統(tǒng)中這次執(zhí)行是由另外一個進程表示的,從執(zhí)行的角度說和父進程相互獨立)中pid=0。這個進程繼續(xù)執(zhí)行的過程中,if語句中 pid<0不滿足,但是pid==0是true。所以輸出i am the child process...
我想你比較困惑的就是,為什么看上去程序中互斥的兩個分支都被執(zhí)行了。在一個程序的一次執(zhí)行中,這當(dāng)然是不可能的;但是你看到的兩行輸出是來自兩個進程,這兩個進程來自同一個程序的兩次執(zhí)行。
我的天,不知道說明白了沒…… ??
zhaojinbo 回復(fù)于:2004-04-28 12:35:50
fork 之后,操作系統(tǒng)會復(fù)制一個與父進程完全相同的子進程,雖說是父子關(guān)系,但是在操作系統(tǒng)看來,他們更像兄弟關(guān)系,這2個進程共享代碼空間,但是數(shù)據(jù)空間是互相獨立的,子進程數(shù)據(jù)空間中的內(nèi)容是父進程的完整拷貝,指令指針也完全相同,但只有一點不同,如果fork成功,子進程中fork的返回值是0,父進程中 fork的返回值是子進程的進程號,如果fork不成功,父進程會返回錯誤。
可以這樣想象,2個進程一直同時運行,而且步調(diào)一致,在fork之后,他們分別作不同的工作,也就是分岔了。這也是fork為什么叫fork的原因。
至于那一個最先運行,可能與操作系統(tǒng)有關(guān),而且這個問題在實際應(yīng)用中并不重要,如果需要父子進程協(xié)同,可以通過原語的辦法解決。
sniper 回復(fù)于:2004-04-28 22:11:15
哦,偶明白了,在程序段里用了fork();之后程序出了分岔,派生出了兩個進程。具體哪個先運行就看該系統(tǒng)的調(diào)度算法了。
在這里,我們可以這么認為,在運行到"pid=fork();"時系統(tǒng)派生出一個跟主程序一模一樣的子進程。該進程的"pid=fork();"一句中 pid得到的就是子進程本身的 pid;子進程結(jié)束后,父進程的"pid=fork();"中pid得到的就是父進程本身的pid。因此該程序有兩行輸出。
注:此處不準(zhǔn)確,在子進程中pid的值為0,通過getpid可以獲取子進程的進程id;在父進程中pid為父進程編號。
勘誤:父進程中的pid值為子進程進程號,只有父進程執(zhí)行的getpid()才是他自己的進程號。寒,徹底的in了??? (yanh_lzu:看了這兩個錯誤的說法,相信大家都不會犯錯誤了。)
jjl3 回復(fù)于:2004-07-14 11:43:20
我做如下修改
#include <unistd.h>;??
#include <sys/types.h>;??
main ()??
{??
????????? pid_t pid;??
????????? printf("fork!");????? // printf("fork!n");
????????? pid=fork();??
????????? if (pid < 0)??
????????????????? printf("error in fork!");??
????????? else if (pid == 0)??
????????????????? printf("i am the child process, my process id is %dn",getpid());??
????????? else??
????????????????? printf("i am the parent process, my process id is %dn",getpid());??
}
??
結(jié)果是??
[root@localhost c]# ./a.out??
fork!i am the child process, my process id is 4286??
fork!i am the parent process, my process id is 4285
但我改成printf("fork!n");后,結(jié)果是
[root@localhost c]# ./a.out
fork!??
i am the child process, my process id is 4286??
i am the parent process, my process id is 4285
為什么只有一個fork!打印出來了?上一個為什么有2個?
bashfulboy 回復(fù)于:2004-07-14 22:10:52
我也來一下:
wujiajia 的理解有些錯誤,
printf("AAAAAAAA");//print 一次;???? 這里會print 2次
如果你將 printf("AAAAAA") 換成 printf("AAAAAAn")???? 那么就是只打印一次了.
主要的區(qū)別是因為有了一個 n??? 回車符號
這就跟Printf的緩沖機制有關(guān)了,printf某些內(nèi)容時,操作系統(tǒng)僅僅是把該內(nèi)容放到了stdout的緩沖隊列里了,并沒有實際的寫到屏幕上
但是,只要看到有 n 則會立即刷新stdout,因此就馬上能夠打印了.
運行了printf("AAAAAA") 后, AAAAAA 僅僅被放到了緩沖里,再運行到fork時,緩沖里面的 AAAAAA 被子進程繼承了
因此在子進程度stdout緩沖里面就也有了 AAAAAA.
所以,你最終看到的會是 AAAAAA 被printf了2次!!!!
而運行 printf("AAAAAAn")后, AAAAAA 被立即打印到了屏幕上,之后fork到的子進程里的stdout緩沖里不會有 AAAAAA 內(nèi)容
因此你看到的結(jié)果會是 AAAAAA 被printf了1次!!!!
(精要)
albcamus 回復(fù)于:2005-03-08 15:56:11
>;>;派生子進程的pid變量并沒有被改變是什么意思 對于子進程來講pid不就是0嗎??
1,派生子進程的進程,即父進程,其pid不變;
2,對子進程來說,fork返回給它0,但它的pid絕對不會是0;之所以fork返回0給它,是因為它隨時可以調(diào)用getpid()來獲取自己的pid;
3,樓上的樓上的你的觀點是對的,fork之后夫子進程除非采用了同步手段,否則不能確定誰先運行,也不能確定誰先結(jié)束。認為子進程結(jié)束后父進程才從fork返回的,這是不對的,fork不是這樣的,vfork才這樣。
看過技術(shù)貼,再來看文檔,就一下子理解了。 ?? 查看下文出處 ???? 第二貼:fork函數(shù)與vfork函數(shù)
| 一、fork 1. 調(diào)用方法 #include <sys/types.h> #include <unistd.h> pid_t fork(void); 正確返回:在父進程中返回子進程的進程號,在子進程中返回0 錯誤返回:-1 ????? 子進程是父進程的一個拷貝。即,子進程從父進程得到了數(shù)據(jù)段和堆棧段的拷貝,這些需要分配新的內(nèi)存;而對于只讀的代碼段,通常使用共享內(nèi)存的方式訪問。fork返回后,子進程和父進程都從調(diào)用fork函數(shù)的下一條語句開始執(zhí)行。 ????? 父進程與子進程的不同之處在于:fork的返回值不同——父進程中的返回值為子進程的進程號,而子進程為0 2. fork函數(shù)調(diào)用的用途 ⑴ 一個進程希望復(fù)制自身,從而父子進程能同時執(zhí)行不同段的代碼。 ⑵ 進程想執(zhí)行另外一個程序 二、vfork 1. 調(diào)用方法 與fork函數(shù)完全相同 #include <sys/types.h> #include <unistd.h> pid_t fork(void); 正確返回:在父進程中返回子進程的進程號,在子進程中返回0 錯誤返回:-1 2. vfork函數(shù)調(diào)用的用途 ??????? 用vfork創(chuàng)建的進程主要目的是用exec函數(shù)執(zhí)行另外的程序,與fork的第二個用途相同 三、fork與vfork的區(qū)別 1. fork要拷貝父進程的數(shù)據(jù)段;而vfork則不需要完全拷貝父進程的數(shù)據(jù)段,在子進程沒有調(diào)用exec和exit之前,子進程與父進程共享數(shù)據(jù)段 2. fork不對父子進程的執(zhí)行次序進行任何限制;而在vfork調(diào)用中,子進程先運行,父進程掛起,直到子進程調(diào)用了exec或exit之后,父子進程的執(zhí)行次序才不再有限制 四、結(jié)束子進程 ????? 結(jié)束子進程不用exit(0),而使用_exit(0)。這是因為_exit(0)在結(jié)束進程時,不對標(biāo)準(zhǔn)I/O流進行任何操作。而exit(0)則會關(guān)閉進程的所有標(biāo)準(zhǔn)I/O流。 |
人家說的理解了,自己來試試看: ????? 下文出處 ????? 首先是:jiangdamalong??? 2006-05-27 17:17:30 在 Linux/Unix社區(qū) / 程序開發(fā)區(qū) 提問 先看一個程序???
??main()???
?? {???
?????? pid_t??? pid;???
?????? if(pid=fork()<0)???
?????? {???
?????????? printf("error!");???
?????? }???
?????? else???
?????? {???
???????????? if(pid==0)???
???????????? printf("a/n");???
???????????? else???
???????????? printf("b/n");???
?????? }???
?? }???
?? 結(jié)果是返回a,b或者b,a???
?? 因為fork調(diào)用將執(zhí)行兩次返回分別從子進程和父進程返回???
?? 由于父進程和子進程無關(guān),父進程與子進程都可能先返回???
????
?? 在看一個程序???
????
?? main()???
?? {???
?????? pid_t??? a_pid,b_fork;???
?????? if(a_pid=fork()<0)???
?????? {???
?????????? printf("error!");???
?????? }???
?????? else???
?????? {???
???????????? if(a_pid==0)???
???????????? printf("b/n");???
???????????? else???
???????????? printf("a/n");???
?????? }???
????
?????? if(b_pid=fork()<0)???
?????? {???
?????????? printf("error!");???
?????? }???
?????? else???
?????? {???
???????????? if(b_pid==0)???
???????????? printf("c/n");???
???????????? else???
???????????? printf("a/n");???
?????? }???
?? }???
????
?? 如果是創(chuàng)建兩個進程則出現(xiàn)結(jié)果???
?? b???
?? c???
?? a???
?? a???
?? c???
?? a???
?? 為什么會返回6個字母??????????? ??? 然后是 anguxiang 的回答: ?????? 事實上,理解fork()的關(guān)鍵在于它的返回點在哪里。fork最特殊的地方就在于他有兩個甚至三個返回值,注意是同時返回兩個值。其中pid=0的這個返回值用來執(zhí)行子進程的代碼,而大于0的一個返回值為父進程的代碼塊。第一次fork調(diào)用的時候生叉分為兩個進程,不妨設(shè)為a父進程和b子進程。他們分別各自在第二次fork調(diào)用之前打印了b和a各一次;在第一次叉分的這兩個進程中都含有???
?? if(b_pid=fork()<0)???
?? {???
?? printf("error!");???
?? }???
?? else???
?? {???
?? if(b_pid==0)???
?? printf("c/n");???
?? else???
?? printf("a/n");???
?? }???
?? }???
?? 這段代碼。很明顯,a父進程和b子進程在這段代碼中又各自獨立的被叉分為兩個進程。這兩個進程每個進程又都打印了a,c各一次。到此,在程序中總共打印三次a兩次c和一次b。總共6個字母。???
?? 注:在第一次叉分為兩個進程的時候父子進程含有完全相同的代碼(第二次仍然相同),只是因為在父子進程中返回的PID的值不同,父進程代碼中的PID的值大于0,子進程代碼中的值等于0,從而通過if這樣的分支選擇語句來執(zhí)行各自的任務(wù)。 ?? 結(jié)束:向上面提到的高人們致敬!
總結(jié)
以上是生活随笔為你收集整理的fork()使用(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 物联网产业链及通信模组详述
- 下一篇: 海康威视实习总结