linux fork函数的精辟解说
此文原文來源于一個blog,文章的名稱為:linux fork函數(shù)的精辟解說原文地址:http://blog.chinaunix.net/space.php?uid=12461657&do=blog&id=3062996感覺這篇文章不錯,在此分享下來,在原文的基礎(chǔ)上增加了自己的一些理解和說明。
開始演示:[plain]view plaincopyprint?
[root@test code]# cat fork.c #include <stdio.h> #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. The process id is %d\n",getpid());else printf("I am the parent process. The process id is %d\n",getpid());} [root@test code]# gcc fork.c -o fork [root@test code]# ./fork I am the child process. The process id is 6260 I am the parent process. The process id is 6259先說什么是進程一個進程,就是一個可執(zhí)行程序的一次執(zhí)行過程中的一個狀態(tài)。
打個比方
我們把一個大學(xué)數(shù)學(xué)老師比作一個可執(zhí)行程序,老師就是一個人,相當(dāng)于一份源碼,這個數(shù)學(xué)老師每個學(xué)期可能要教多個班,假設(shè)他教1班和2班,時間是一個學(xué)期,那么他從開學(xué)到期末教這兩個班這個過程就是兩個進程,兩個進程的周期都是一個學(xué)期。
在稍微理解了進程的概念之后,我們說在正在運行的計算機中,不管是Linux或者是Windows系統(tǒng)都運行有很多進程,雖然我們的系統(tǒng)中進程比較多,但是對于單核的CPU而言,每一時刻只能有一個進程占用CPU,其他的進程就可能處在等待執(zhí)行、就緒、結(jié)束等狀態(tài)(狀態(tài)名稱可能隨不同操作系統(tǒng)而相異)。
那么要使我們的操作系統(tǒng)能夠正常執(zhí)行,操作系統(tǒng)對這些進程的管理,典型的情況,是通過內(nèi)存中的進程表完成的(這里的進程表不是源碼的意思)。進程表中的每一個表項,記錄的是當(dāng)前操作系統(tǒng)中一個進程的情況,比如執(zhí)行到哪里,下一步要執(zhí)行什么命令等。
一個稱為“程序計數(shù)器(program counter, pc)”的寄存器,指出當(dāng)前占用CPU的進程要執(zhí)行的下一條指令的位置。當(dāng)分給某個進程的CPU時間已經(jīng)用完,操作系統(tǒng)將該進程相關(guān)的寄存器的值,保存到該進程在進程表中對應(yīng)的表項里面;然后把將要接替掉當(dāng)前進程的那個進程的上下文從內(nèi)存的進程表中讀入,并更新相應(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)建一個新的子進程,而這個子進程是父進程的副本,接下來這兩個進行就由操作系統(tǒng)調(diào)度,直到程序執(zhí)行結(jié)束。
那么這個過程,我們可以對其進行更加詳細的分析。在執(zhí)行fork以后,操作系統(tǒng)復(fù)制一份當(dāng)前執(zhí)行的進程的數(shù)據(jù),包括進程的數(shù)據(jù)空間、堆和棧等,并且在進程表中相應(yīng)為它建立一個新的表項。上下文也是原進程(父進程)的拷貝。但是父、子進程共享正文段,也就是CPU執(zhí)行的機器指令部分,這個可共享的,在存儲器中只需要一個副本,而且這個副本通常是只讀的。
那在什么時候父子進程中就分道揚鑣呢?
從上面的實驗結(jié)果我們看出那兩條打印出來的語句不是在同一個進程里面的,因為在同一個進程里面不可能存在,不通結(jié)果的getpid(),事實說明是兩個不同進程返回的結(jié)果,那么在執(zhí)行pid=fork()以后,開始出現(xiàn)父、子進程,既然是兩個進程,那么接下來誰先被調(diào)度,也就是說執(zhí)行pid=fork()以后,在單核CPU下,只會有一個進程被調(diào)度,假設(shè)是我們的父進程占用CPU時間,父進程繼續(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)度,它的上下文被換入。我們上面分析過,在子進行創(chuàng)建的時候也會復(fù)制父進程的上下文,所以子進程不會從頭開始執(zhí)行,而是從pid=fork()開始執(zhí)行,基于操作系統(tǒng)對fork的實現(xiàn),使得子進程中fork調(diào)用返回0.所以在這個進程(中pid=0.這個進程繼續(xù)執(zhí)行的過程中,if語句中 pid<0不滿足,但是pid==0是true.所以輸出I am the child process……。
我們下面來看一個和我在上面分析的一個結(jié)論似乎存在矛盾的現(xiàn)象
[plain]view plaincopyprint?
[root@test code]# cat fork.c #include <stdio.h> #include <unistd.h> #include <sys/types.h>
main()
{ pid_t pid;printf("fork!");pid=fork();if (pid < 0)
printf("Error in fork!\n");else if (pid == 0)
printf("I am the child process. The process id is %d\n",getpid());else printf("I am the parent process. The process id is %d\n",getpid());} [root@test code]# gcc fork.c -o fork [root@test code]# ./fork fork!I am the child process. The process id is 7378 fork!I am the parent process. The process id is 7377 [root@test code]#
這里我添加了printf("fork!")這一行,執(zhí)行了以后我們發(fā)現(xiàn),“fork!”打印了兩次,我們上面不是說,fork以后的子進程的上下文不是和父進程一樣嗎,也就是說子進程不會從頭開始執(zhí)行,應(yīng)該從fork執(zhí)行,那么fork!的出現(xiàn)不是有矛盾嗎?我們再來看看下面的現(xiàn)象
[plain]view plaincopyprint?
<span style="font-family:Courier New;">[root@test code]# cat fork.c #include <stdio.h> #include <unistd.h> #include <sys/types.h>
main()
{ pid_t pid;printf("fork!\n");pid=fork();if (pid < 0)
printf("Error in fork!\n");else if (pid == 0)
printf("I am the child process. The process id is %d\n",getpid());else printf("I am the parent process. The process id is %d\n",getpid());} [root@test code]# gcc fork.c -o fork [root@test code]# ./fork fork!
I am the child process. The process id is 7458 I am the parent process. The process id is 7457 [root@test code]#</span>
這里我在printf("fork!")這一行的fork!后面添加了一個換行符,變成printf("fork!\n")執(zhí)行以后發(fā)現(xiàn)只打印一個fork!這個到底是什么原因呢?
主要的區(qū)別是因為有了一個\n 回車符號,說起真正的原因,這和printf的緩沖機制有關(guān)了,printf某些內(nèi)容時,操作系統(tǒng)僅僅是把該內(nèi)容放到了stdout的緩沖隊列里了,并沒有實際的寫到屏幕上,但是只要看到有\(zhòng)n 則會立即刷新stdout,因此就馬上能夠打印了。
還沒有執(zhí)行到fork()的時候,原進程運行了printf("fork!") 后,fork!僅僅被放到了緩沖里,執(zhí)行了fork,這時候子進程復(fù)制一份父進程的數(shù)據(jù),包括這個stdout緩沖,在子進程度stdout緩沖里面就也有了fork!。執(zhí)行到后面這兩個緩沖都打印到屏幕上,所以出現(xiàn)兩次,并不是printf執(zhí)行兩次。而運行 printf("fork!\n")后, fork!被立即打印到了屏幕上,之后fork到的子進程里的stdout緩沖里不會有fork!內(nèi)容,因此你看到的結(jié)果會是fork!被printf了1次!!!
總結(jié)
以上是生活随笔為你收集整理的linux fork函数的精辟解说的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Evernote,有道云笔记以及Ms O
- 下一篇: C#中e.Handle是什么意思