国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
fork函數(shù)返回值淺談
fork函數(shù)返回值淺談fork簡介:
fork英文原意是“分岔,分支”的意思,而在操作系統(tǒng)中,乃是著名的Unix(或類Unix,如Linux,Minix)中用于創(chuàng)建子進程的系統(tǒng)調用。
【NOTE1】
fork () 的作用是什么?換句話說,你用 fork () 的目的是什么?
――是為了產(chǎn)生一個新的進程,地球人都知道 :)
產(chǎn)生一個什么樣的進程?
――和你本來調用 fork () 的那個進程基本一樣的進程,其實就是你原來進程的副本;
真的完全一樣嗎?
――當然不能完全一樣,你要兩個除了 pid 之外其它一模一樣的進程干什么,就算memory
再多也不用這么擺譜吧?
哪里不一樣?
――當然最重要的是 fork () 之后執(zhí)行的代碼不一樣,you know, i know :)
怎么實現(xiàn)呢?
――如果是 Windows,它會讓你在 fork () 里面提供一大堆東西,指明這個那個什么的……
我用的是 unix 啊
――所以很簡單,unix 會讓兩個進程(不錯,原來是一個,unix 替你復制了一個,現(xiàn)在有兩個)
在 fork () 之后產(chǎn)生不同:返回值不同。其中一個進程(使用新的 pid)里面的 fork () 返回零,
這個進程就是“子進程”;而另一個進程(使用原來的 pid)中的 fork () 返回前面那個子進程的
pid,他自己被稱為“父進程”
然后呢?
――寫代碼的人又不笨,當然就根據(jù)返回值是否非零來判斷了,現(xiàn)在我是在子進程里面呢,還是在
父進程里面?在子進程里面就執(zhí)行子進程該執(zhí)行的代碼,在父進程里面就執(zhí)行父進程的代碼……
有鐵桿 windows fans 借此說明,windows 好啊,子進程用子進程的代碼,父進程用父進程的,
你 unix 笨了吧,子進程包含父進程、子進程的代碼,父進程包含父進程子進程的代碼,豈不是多占用內存了嗎?
――據(jù)我所知,unix 代碼段都是可重入代碼,也就是說,進程復制,并不復制代碼段,若干個進程
共享同一代碼段,增加的只是全局共享數(shù)據(jù)和對文件描述符的引用等,另外就是堆棧。你一個代碼
長達 10M 的進程,fork () 出三四個子進程,只是增加一點內存占用(如果你沒有使用很多全局變量
的話),而不是占用 40M 以上的內存。
【NOTE2】
程序 從 fork 開始分支 (稱分支不準確), 一路是主進程 pid > 0 (pid 是子進程ID) 一路是子進程 pid == 0 自此分成兩個任務
其實fork的時候已經(jīng)兩個分支了,數(shù)據(jù)段被復制了一份,因此pid有兩份
執(zhí)行pid=fork()時,返回值賦給pid在兩個進程中運行,
fork會返回給父進程的那個>0的值,告訴調用者新建進程的pid
子進程的fork返回值是0
更不用說if...else的比較也是在兩個進程中都做的了
【NOTE3】
fork的精辟剖析
程序如下:
#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());
}
結果是
[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
一:
要搞清楚fork的執(zhí)行過程,就必須先講清楚操作系統(tǒng)中的“進程(process)”概念。一個進程,主要包含三個元素:
o. 一個可以執(zhí)行的程序;
o. 和該進程相關聯(lián)的全部數(shù)據(jù)(包括變量,內存空間,緩沖區(qū)等等);
o. 程序的執(zhí)行上下文(execution context)。
不妨簡單理解為,一個進程表示的,就是一個可執(zhí)行程序的一次執(zhí)行過程中的一個狀態(tài)。操作系統(tǒng)對進程的管理,典型的情況,是通過進程表完成的。進程表中的每一個表項,記錄的是當前操作系統(tǒng)中一個進程的情況。對于單 CPU的情況而言,每一特定時刻只有一個進程占用 CPU,但是系統(tǒng)中可能同時存在多個活動的(等待執(zhí)行或繼續(xù)執(zhí)行的)進程。一個稱為“程序計數(shù)器(program counter, pc)”的寄存器,指出當前占用 CPU的進程要執(zhí)行的下一條指令的位置。當分給某個進程的 CPU時間已經(jīng)用完,操作系統(tǒng)將該進程相關的寄存器的值,保存到該進程在進程表中對應的表項里面;把將要接替這個進程占用 CPU的那個進程的上下文,從進程表中讀出,并更新相應的寄存器(這個過程稱為“上下文交換(process context switch)”,實際的上下文交換需要涉及到更多的數(shù)據(jù),那和fork無關,不再多說,主要要記住程序寄存器 pc指出程序當前已經(jīng)執(zhí)行到哪里,是進程上下文的重要內容,換出 CPU的進程要保存這個寄存器的值,換入CPU的進程,也要根據(jù)進程表中保存的本進程執(zhí)行上下文信息,更新這個寄存器)。
好了,有這些概念打底,可以說fork了。當你的程序執(zhí)行到下面的語句:
fork函數(shù)返回值淺談
pid=fork();
操作系統(tǒng)創(chuàng)建一個新的進程(子進程),并且在進程表中相應為它建立一個新的表項。新進程和原有進程的可執(zhí)行程序是同一個程序;上下文和數(shù)據(jù),絕大部分就是原進程(父進程)的拷貝,但它們是兩個相互獨立的進程!此時程序寄存器pc,在父、子進程的上下文中都聲稱,這個進程目前執(zhí)行到fork調用即將返回(此時子進程不占有CPU,子進程的pc不是真正保存在寄存器中,而是作為進程上下文保存在進程表中的對應表項內)。問題是怎么返回,在父子進程中就分道揚鑣。
父進程繼續(xù)執(zhí)行,操作系統(tǒng)對fork的實現(xiàn),使這個調用在父進程中返回剛剛創(chuàng)建的子進程的pid(一個正整數(shù)),所以下面的if語句中pid<0, pid==0的兩個分支都不會執(zhí)行。所以輸出i am the parent process...
子進程在之后的某個時候得到調度,它的上下文被換入,占據(jù) CPU,操作系統(tǒng)對fork的實現(xiàn),使得子進程中fork調用返回0。所以在這個進程(注意這不是父進程了哦,雖然是同一個程序,但是這是同一個程序的另外一次執(zhí)行,在操作系統(tǒng)中這次執(zhí)行是由另外一個進程表示的,從執(zhí)行的角度說和父進程相互獨立)中pid=0。這個進程繼續(xù)執(zhí)行的過程中,if語句中 pid<0不滿足,但是pid= =0是true。所以輸出i am the child process...
為什么看上去程序中互斥的兩個分支都被執(zhí)行了?在一個程序的一次執(zhí)行中,這當然是不可能的;但是你看到的兩行輸出是來自兩個進程,這兩個進程來自同一個程序的兩次執(zhí)行。
fork之后,操作系統(tǒng)會復制一個與父進程完全相同的子進程,雖說是父子關系,但是在操作系統(tǒng)看來,他們更像兄弟關系,這2個進程共享代碼空間,但是數(shù)據(jù)空間是互相獨立的,子進程數(shù)據(jù)空間中的內容是父進程的完整拷貝,指令指針也完全相同,但只有一點不同,如果fork成功,子進程中fork的返回值是0,父進程中fork的返回值是子進程的進程號,如果fork不成功,父進程會返回錯誤。
可以這樣想象,2個進程一直同時運行,而且步調一致,在fork之后,他們分別作不同的工作,也就是分岔了。這也是fork為什么叫fork的原因。
在程序段里用了fork()之后程序出了分岔,派生出了兩個進程。具體哪個先運行就看該系統(tǒng)的調度算法了。
如果需要父子進程協(xié)同,可以通過原語的辦法解決。
二:
進程的創(chuàng)建:
創(chuàng)建一個進程的系統(tǒng)調用很簡單.我們只要調用fork函數(shù)就可以了.
#include <unistd.h>
pid_t fork();
當一個進程調用了fork以后,系統(tǒng)會創(chuàng)建一個子進程.這個子進程和父進程不同的地方只有他的進程ID和父進程ID,其他的都是一樣.就象父進程克隆 (clone)自己一樣.當然創(chuàng)建兩個一模一樣的進程是沒有意義的.為了區(qū)分父進程和子進程,我們必須跟蹤fork的返回值. 當fork掉用失敗的時候(內存不足或者是用戶的最大進程數(shù)已到)fork返回-1,否則fork的返回值有重要的作用.對于父進程fork返回子進程的 ID,而對于fork子進程返回0.我們就是根據(jù)這個返回值來區(qū)分父子進程的. 父進程為什么要創(chuàng)建子進程呢?前面我們已經(jīng)說過了Linux是一個多用戶操作系統(tǒng),在同一時間會有許多的用戶在爭奪系統(tǒng)的資源.有時進程為了早一點完成任務就創(chuàng)建子進程來爭奪資源. 一旦子進程被創(chuàng)建,父子進程一起從fork處繼續(xù)執(zhí)行,相互競爭系統(tǒng)的資源.有時候我們希望子進程繼續(xù)執(zhí)行,而父進程阻塞,直到子進程完成任務.這個時候我們可以調用wait或者waitpid系統(tǒng)調用.
總結一下有三:
1,派生子進程的進程,即父進程,其pid不變;
2,對子進程來說,fork返回給它0,但它的pid絕對不會是0;之所以fork返回0給它,是因為它隨時可以調用getpid()來獲取自己的pid;
3,fork之后父子進程除非采用了同步手段,否則不能確定誰先運行,也不能確定誰先結束。認為子進程結束后父進程才從fork返回的,這是不對的,fork不是這樣的,vfork才這樣。

【NOTE4】
首先必須有一點要清楚,函數(shù)的返回值是儲存在寄存器eax中的。
其次,當fork返回時,新進程會返回0是因為在初始化任務結構時,將eax設置為0;
在fork中,把子進程加入到可運行的隊列中,由進程調度程序在適當?shù)臅r機調度運行。也就是從此時開始,當前進程分裂為兩個并發(fā)的進程。
無論哪個進程被調度運行,都將繼續(xù)執(zhí)行fork函數(shù)的剩余代碼,執(zhí)行結束后返回各自的值。
【NOTE5】
對于fork來說,父子進程共享同一段代碼空間,所以給人的感覺好像是有兩次返回,其實對于調用fork的父進程來說,如果fork出來的子進程沒有得到調度,那么父進程從fork系統(tǒng)調用返回,同時分析sys_fork知道,fork返回的是子進程的id。再看fork出來的子進程,由 copy_process函數(shù)可以看出,子進程的返回地址為ret_from_fork(和父進程在同一個代碼點上返回),返回值直接置為0。所以當子進程得到調度的時候,也從fork返回,返回值為0。
關鍵注意兩點:1.fork返回后,父進程或子進程的執(zhí)行位置。(首先會將當前進程eax的值做為返回值)2.兩次返回的pid存放的位置。(eax中)
進程調用copy_process得到lastpid的值(放入eax中,fork正常返回后,父進程中返回的就是lastpid)
子進程任務狀態(tài)段tss的eax被設置成0,
fork.c 中
p->tss.eax=0;(如果子進程要執(zhí)行就需要進程切換,當發(fā)生切換時,子進程tss中的eax值就調入eax寄存器,子進程執(zhí)行時首先會將eax的內容做為返回值)
當子進程開始執(zhí)行時,copy_process返回eax的值。
fork()后,就是兩個任務同時進行,父進程用他的tss,子進程用自己的tss,在切換時,各用各的eax中的值.
所以,“一次調用兩次返回”是2個不同的進程!
看這一句:pid=fork()
當執(zhí)行這一句時,當前進程進入fork()運行,此時,fork()內會用一段嵌入式匯編進行系統(tǒng)調用:int 0x80(具體代碼可參見內核版本0.11的unistd.h文件的133行_syscall0函數(shù))。這時進入內核根據(jù)此前寫入eax的系統(tǒng)調用功能號便會運行sys_fork系統(tǒng)調用。接著,sys_fork中首先會調用C函數(shù)find_empty_process產(chǎn)生一個新的進程,然后會調用C函數(shù) copy_process將父進程的內容復制給子進程,但是子進程tss中的eax值賦值為0(這也是為什么子進程中返回0的原因),當賦值完成后, copy_process會返回新進程(該子進程)的pid,這個值會被保存到eax中。這時子進程就產(chǎn)生了,此時子進程與父進程擁有相同的代碼空間,程序指針寄存器eip指向相同的下一條指令地址,當fork正常返回調用其的父進程后,因為eax中的值是新創(chuàng)建的子進程號,所以,fork()返回子進程號,執(zhí)行else(pid>0);當產(chǎn)生進程切換運行子進程時,首先會恢復子進程的運行環(huán)境即裝入子進程的tss任務狀態(tài)段,其中的eax 值(copy_process中置為0)也會被裝入eax寄存器,所以,當子進程運行時,fork返回的是0執(zhí)行if(pid==0)。

本文來自: (www.91linux.com) 詳細出處參考:http://www.91linux.com/html/article/program/cpp/20100803/20033_2.html
【NOTE5】
理解它關鍵在于理解堆棧的切換和壓棧,彈棧!
關于子進程的返回:
子進程復制了父進程的棧內容,從高到低
SS
ESP
EFLAGS
CS
EIP -----此是int 0x80 的下一條指令,也是子進程開始執(zhí)行的地方?。。?!
DS
ES
FS
EDX
ECX
EBX
GS
ESI
EDI
EBP
EAX(0)
由于 EAX = 0,所以子進程返回 0 給 fork.
注:新進程的用戶棧設為其父進程的用戶棧(最后彈出的SS,ESP)。如果父子進程以copy_on_write方式共用用戶堆棧
(Linux之下就是這樣的),而且在此之前父進程修改了該堆棧(如果父進程先返回,這幾乎是肯定的),那么,系統(tǒng)已經(jīng)為父進程創(chuàng)建了該用戶棧的副本,父進程原來的用戶棧留給了子進程。那么新進程的系統(tǒng)棧已經(jīng)清空,新進程回到了用戶態(tài),返回到了函數(shù)fork。
【NOTE6】
關于fork的討論與評價:
fork好不好?相比其他操作系統(tǒng)如Windows,Windows會有諸如CreateProcess這樣的函數(shù)來創(chuàng)建一個與生俱來兩手空空的獨立的新進程。然后還有一大堆參數(shù),指手畫腳的告訴你這個那個是什么。。。
煩!!!
K.I.S.S. (Keep it simple,stupid.)是Unix的至高原則。
fork 起源于 Unix 操作系統(tǒng)。那是貝爾實驗室的 K&R (這兩人是Unix和C語言之父) 的一項天才發(fā)明!!! Linux由于與生俱來就與Unix血濃于水,所以繼承了它的這個天才發(fā)明。
這種方法效率是很高的。因為復制的代價是很低的。在計算機網(wǎng)絡的實現(xiàn)中,以及在 client/server 系統(tǒng)中的server 一方的實現(xiàn)中,fork 常常是最自然,最有效,最適宜的手段。很多人甚至懷疑,到底是先有 fork 還是先有 client/server,因為 fork 似乎就是專門為此而設計的 !更重要的好處是,這樣有利于父子進程間通過 pipe 建立起一種簡單有效的進程間通信管道,并且產(chǎn)生了操作系統(tǒng)的用戶界面即 shell 的管道機制。這一點,對于 Unix 的發(fā)展和應用推廣,對于 Uinx 程序設計環(huán)境的形成,對于 Unix 程序設計風格的形成,都有非常深遠的影響。可以說這是一項天才的發(fā)明,它在很大程度上改變了操作系統(tǒng)的發(fā)展方向。
本站僅提供存儲服務,所有內容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Linux 內核
fork后子進程從哪里開始執(zhí)行
fork如何做到返回兩次
關于fork的討論
【Linux操作系統(tǒng)分析】進程的創(chuàng)建與可執(zhí)行程序的加載
fork函數(shù)剖析
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服