Linux fork() 和 vfork()
代碼
#include <unistd.h> #include <sys/types.h> #include <iostream> #include <errno.h>int main() {pid_t pid;pid = fork();if (pid < 0)std::cout << "error in fork! errno = " << errno << std::endl;else if (pid == 0)std::cout << "I am the child process, my process id is " << getpid() << std::endl;elsestd::cout << "I am the parent process, my process id is " << getpid() << std::endl;return 0; }結果
I am the parent process, my process id is 24275 I am the child process, my process id is 24276對于 fork() 函數,在父進程中返回子進程的 PID( > 0 ),在子進程中返回 0 。
1、那么 fork() 函數是如何實現在不同的進程中返回不同的結果呢?
答案:
當你的程序執行到下面的語句:?
int pid = fork();?操作系統創建一個新的進程(子進程),并且在進程表中相應為它建立一個新的表項。新進程和原有進程的可執行程序是同一個程序。上下文和數據,絕大部分就是 原進程(父進程)的拷貝,但它們是兩個相互獨立的進程!此時程序寄存器 pc,在父、子進程的上下文中都聲稱,這個進程目前執行到 fork 調用即將返回(此時子進程不占有CPU,子進程的 pc 不是真正保存在寄存器中,而是作為進程上下文保存在進程表中的對應表項內)。問題是怎么返回,在父子進程中就分道揚鑣了呢?
父進程繼續執行,操作系統對 fork 的實現,使這個調用在父進程中返回剛剛創建的子進程的 PID(一個正整數),所以下面的 if 語句中 pid < 0 , pid == 0 的兩個分支都不會執行。所以輸出 “I am the parent process...”。?
子進程在之后的某個時候得到調度,它的上下文被換入,占據 CPU,操作系統對 fork 的實現,使得子進程中 fork 調用返回0。所以在這個進程(注意這不是父進程了哦,雖然是同一個程序,但是這是同一個程序的另外一次執行,在操作系統中這次執行是由另外一個進程表示的,從執行的角度說和父進程相互獨立)中 pid = 0 。這個進程繼續執行的過程中,if 語句中 pid < 0 不滿足,但是 pid == 0 是 true。所以輸出 “I am the child process...”。?
為什么看上去程序中互斥的兩個分支都被執行了?在一個程序的一次執行中,這當然是不可能的;但是你看到的兩行輸出是來自兩個進程,這兩個進程來自同一個程序的兩次執行。?
fork 之后,操作系統會復制一個與父進程完全相同的子進程,雖說是父子關系,但是在操作系統看來,他們更像兄弟關系,這 2 個進程共享代碼空間,但是數據 空間是互相獨立的,子進程數據空間中的內容是父進程的完整拷貝,指令指針也完全相同,但只有一點不同,如果 fork 成功,子進程中 fork 的返回值是 0 , 父進程中 fork 的返回值是子進程的進程號,如果 fork 不成功,父進程會返回錯誤。?
可以這樣想象,2 個進程一直同時運行,而且步調一致,在 fork 之后,他們分別作不同的工作,也就是分岔了。這也是 fork為什么叫 fork 的原因。
在程序段里用了 fork() 之后程序出了分岔,派生出了兩個進程。具體哪個先運行就看該系統的調度算法了。
2、fork() 執行過程。
看這一句:pid=fork() 。
當執行這一句時,當前進程進入 fork() 運行,此時,fork() 內會用一段嵌入式匯編進行系統調用:int 0x80(具體代碼可參見內核版本 0.11 的 unistd.h 文件的 133 行 _syscall0 函數)。
這時進入內核根據此前寫入 eax 的系統調用功能號 便會運行 sys_fork 系統調用。
接著,sys_fork?中首先會調用 C 函數 find_empty_process 產生一個新的進程,然后會調用 C 函數 copy_process 將父進程的內容復制給子進程,但是子進程 tss 中的 eax 值賦值為 0(這也是為什么子進程中返回 0 的原因)。當賦值完成后, copy_process 會返回新進程(該子進程)的 pid,這個值會被保存到 eax 中。
這時子進程就產生了,此時子進程與父進程擁有相同的代碼空間,程序指針寄存器 eip 指向相同的下一條指令地址,當 fork 正常返回調用其的父進程后,因為 eax 中的值是新創建的子進程號。所以,fork()返回子進程號,執行 else(pid > 0)。
當產生進程切換運行子進程時,首先會恢復子進程的運行環境即裝入子進程的 tss 任務狀態段,其中的 eax 值( copy_process 中置為 0 )也會被裝入 eax 寄存器。所以,當子進程運行時,fork 返回的是 0 執行 if( pid == 0 )。
3、fork() 和 vfork()?
二者的功能都是創建一個新的進程,但是二者的區別還是比較大的,主要部分如下:
(1)前者創建完進程之后,父進程由于占用了CPU的時間片,所以一般父進程先返回。當然了,如果父子進程都準備好了要返回了,但是此時 CPU 開始輪詢了,那么最后誰先返回就不確定了。總的來說,父子進程誰先執行是不確定的。對于后者,能夠保證子進程執行 exec 和 exit 之前,父進程處于休眠狀態,即:子進程先執行。
(2)前者在創建子進程時,子進程會復制掉父進程的數據段、堆、棧(代碼段共享)(Copy on Write)。后者的子進程在執行 exec 和 exit 之前,會與父進程共用父進程的虛擬地址空間。
?
(SAW:Game Over!)
總結
以上是生活随笔為你收集整理的Linux fork() 和 vfork()的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux IPC / 分类
- 下一篇: exit()、_Exit() 和 _ex