文章的名稱為:linux fork函式的精闢解說
開始演示:[plain]view plaincopyprint?
[root@test code]# cat fork.c #include #include #include
main()
[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先說什麼是程序乙個程序,就是乙個可執行程式的一次執行過程中的乙個狀態。
打個比方
我們把乙個大學數學老師比作乙個可執行程式,老師就是乙個人,相當於乙份原始碼,這個數學老師每個學期可能要教多個班,假設他教1班和2班,時間是乙個學期,那麼他從開學到期末教這兩個班這個過程就是兩個程序,兩個程序的週期都是乙個學期。
在稍微理解了程序的概念之後,我們說在正在執行的計算機中,不管是linux或者是windows系統都執行有很多程序,雖然我們的系統中程序比較多,但是對於單核的cpu而言,每一時刻只能有乙個程序占用cpu,其他的程序就可能處在等待執行、就緒、結束等狀態(狀態名稱可能隨不同作業系統而相異)。
那麼要使我們的作業系統能夠正常執行,作業系統對這些程序的管理,典型的情況,是通過記憶體中的程序表完成的(這裡的程序表不是原始碼的意思)。程序表中的每乙個表項,記錄的是當前作業系統中乙個程序的情況,比如執行到**,下一步要執行什麼命令等。
乙個稱為「程式計數器(program counter, pc)」的暫存器,指出當前占用cpu的程序要執行的下一條指令的位置。當分給某個程序的cpu時間已經用完,作業系統將該程序相關的暫存器的值,儲存到該程序在程序表中對應的表項裡面;然後把將要接替掉當前程序的那個程序的上下文從記憶體的程序表中讀入,並更新相應的暫存器。這個過程稱為「上下文交換(process context switch)」,實際的上下文交換需要涉及到更多的資料,那和fork無關,不再多說,主要要記住程式暫存器pc指出程式當前已經執行到**,是程序上下文的重要內容,換出 cpu的程序要儲存這個暫存器的值,換入cpu的程序,也要根據程序表中儲存的本程序執行上下文資訊,更新這個暫存器)。
好了,有這些概念打底,可以說fork了。當你的程式執行到下面的語句:pid=fork();作業系統會進行如下的大概過程:建立乙個新的子程序,而這個子程序是父程序的副本,接下來這兩個進行就由作業系統排程,直到程式執行結束。
那麼這個過程,我們可以對其進行更加詳細的分析。在執行fork以後,作業系統複製乙份當前執行的程序的資料,包括程序的資料空間、堆和棧等,並且在程序表中相應為它建立乙個新的表項。上下文也是原程序(父程序)的拷貝。但是父、子程序共享正文段,也就是cpu執行的機器指令部分,這個可共享的,在儲存器中只需要乙個副本,而且這個副本通常是唯讀的。
那在什麼時候父子程序中就分道揚鑣呢?
從上面的實驗結果我們看出那兩條列印出來的語句不是在同乙個程序裡面的,因為在同乙個程序裡面不可能存在,不通結果的getpid(),事實說明是兩個不同程序返回的結果,那麼在執行pid=fork()以後,開始出現父、子程序,既然是兩個程序,那麼接下來誰先被排程,也就是說執行pid=fork()以後,在單核cpu下,只會有乙個程序被排程,假設是我們的父程序占用cpu時間,父程序繼續執行,作業系統對fork的實現,使這個呼叫在父程序中返回剛剛建立的子程序的pid(乙個正整數),所以下面的if語句中pid<0, pid==0的兩個分支都不會執行。所以輸出i am the parent process……。
子程序在之後的某個時候得到排程,它的上下文被換入。我們上面分析過,在子進行建立的時候也會複製父程序的上下文,所以子程序不會從頭開始執行,而是從pid=fork()開始執行,基於作業系統對fork的實現,使得子程序中fork呼叫返回0.所以在這個程序(中pid=0.這個程序繼續執行的過程中,if語句中 pid<0不滿足,但是pid==0是true.所以輸出i am the child process……。
我們下面來看乙個和我在上面分析的乙個結論似乎存在矛盾的現象
[plain]view plaincopyprint?
[root@test code]# cat fork.c #include #include #include
main()
[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!")這一行,執行了以後我們發現,「fork!」列印了兩次,我們上面不是說,fork以後的子程序的上下文不是和父程序一樣嗎,也就是說子程序不會從頭開始執行,應該從fork執行,那麼fork!的出現不是有矛盾嗎?我們再來看看下面的現象
[plain]view plaincopyprint?
[root@test code]# cat fork.c #include #include #include
main()
[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]#
這裡我在printf("fork!")這一行的fork!後面新增了乙個換行符,變成printf("fork!\n")執行以後發現只列印乙個fork!這個到底是什麼原因呢?
主要的區別是因為有了乙個\n 回車符號,說起真正的原因,這和printf的緩衝機制有關了,printf某些內容時,作業系統僅僅是把該內容放到了stdout的緩衝佇列裡了,並沒有實際的寫到螢幕上,但是只要看到有\n 則會立即重新整理stdout,因此就馬上能夠列印了。
還沒有執行到fork()的時候,原程序執行了printf("fork!") 後,fork!僅僅被放到了緩衝裡,執行了fork,這時候子程序複製乙份父程序的資料,包括這個stdout緩衝,在子進程度stdout緩衝裡面就也有了fork!。執行到後面這兩個緩衝都列印到螢幕上,所以出現兩次,並不是printf執行兩次。而執行 printf("fork!\n")後, fork!被立即列印到了螢幕上,之後fork到的子程序裡的stdout緩衝裡不會有fork!內容,因此你看到的結果會是fork!被printf了1次!!!
printf("hello!");
//fflush(stdout);
fork();
輸出:hello!hello!
printf("hello!");
fflush(stdout);
fork();
輸出:hello!
linux fork函式的精闢解說
開始演示 plain view plaincopyprint?root test code cat fork.c include include include main root test code gcc fork.c o fork root test code fork i am the ch...
linux fork函式與vfork函式
一 fork 1.呼叫方法 include include pid t fork void 正確返回 在父程序中返回子程序的程序號,在子程序中返回0 錯誤返回 1 子程序是父程序的乙個拷貝。即,子程序從父程序得到了資料段和堆疊段的拷貝,這些需要分配新的記憶體 而對於唯讀的 段,通常使用共享記憶體的方...
linux fork 函式學習
分類 專業學習 include include include int main case 0 default printf n d n n return 0 輸出結果1 fork child i am child parent i am parent parent getpid 4496 pare...