fork與vfork函式
一、fork函式
pid_t fork(void); 返回值:子程式中返回0,父程式中返回子程式ID,出錯返回-1。
一個現有程式可以呼叫fork建立一個新程式。
子程式是父程式的副本。例如:子程式獲得父程式資料空間、堆和棧的副本(主要是資料結構的副本)。父子程式不共享這些儲存空間部分。父子程式共享正文段。 由於fork之後經常歸屬exec,所以現在很多實現並不執行一個父程式資料段、棧和堆的完全複製。作為替代,使用了寫時複製(Copy-On-Write)技術。這些區域由父子程式共享,而且核心將他們的訪問許可權改變為只讀的。如果父子程式中的任一個試圖修改這些區域,則核心只為修改區域的那塊記憶體製作一個副本。
演示fork函式:
#include<stdio.h>
#include<unistd.h>
int glob = 6;
char buf[] = "a write to stdout\n";
int main()
{
int var;
pid_t pid;
var = 88;
if(write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
perror("write error");
printf("before fork\n");
pid = fork();
if(pid < 0)
{
perror("fork error");
}
else if(pid == 0) //child
{
glob++;
var++;
}
else //parent
{
sleep(2);
}
printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var);
return 0;
}
執行結果:
由執行結果可以看出,子程式對變數所作的改變並不影響父程式中該變數的值。
一般來說fork之後父程式和子程式的執行順序是不確定的,這取決於核心的排程演算法。在上面的程式中,父程式是自己休眠2秒鐘,以使子程式先執行。程式中fork與I/O函式之間的關係:write是不帶緩衝的,因為在fork之前呼叫write,所以其資料只寫到標準輸出一次。標準I/O是緩衝的,如果標準輸出到終端裝置,則它是行緩衝,否則它是全緩衝。當以互動方式執行該程式時,只得到printf輸出的行一次,因為標準輸出到終端緩衝區由換行符沖洗。但將標準輸出重定向到一個⽂檔案時,由於緩衝區是全緩衝,遇到換行符不輸出,當呼叫fork時,其printf的資料仍然在緩衝區中,該資料將被複制到子程式中,該緩衝區也被複制到子程式中。於是父子程式的都有了帶改行內容的標準 I/O緩衝區,所以每個程式終止時,會沖洗其緩衝區中的資料,得到第一個printf輸出兩次。
fork的一個特性是父程式的所有開啟檔案描述符都被複制到子程式中。父子程式的每個相同的開啟描述符共享一個檔案表項。假設一個程式有三個不同的開啟檔案,在從fork返回時,有如下所示結構:
在fork之後處理的檔案描述符有兩種常見的情況:
1. 父程式等待子程式完成。在這種情況下,父程式無需對其描述符做任何處理。當子程式終止後,子程式對檔案偏移量的修改已執行的更新。
2. 父子程式各自執行不同的程式段。這種情況下,在fork之後,父子程式各自關閉他們不需要使用的檔案描述符,這樣就不會干擾對方使用檔案描述符。這種方法在網路服務程式中經常使用。
父子程式之間的區別:
1. fork的返回值
2. 程式ID不同
3. 具有不同的父程式ID
4. 子程式的tms_utime、tms_stime、tms_cutime及tms_ustime均被設定為0
5. 父程式設定的檔案鎖不會被子程式繼承
6. 子程式的未處理鬧鐘被清除
7. 子程式的未處理訊號集被設定為空集
fork有下面兩種用法:
1. 一個父程式希望複製自己,使父子程式同時執行不同的程式碼段。例如,父程式等待客戶端請求,生成子程式來處理請求。
2. 一個程式要執行一個不同的程式。例如子程式從fork返回後,呼叫exec函式。
fork呼叫失敗的原因:
1. 系統中已經有了太多的程式
2. 實際使用者ID的程式總數超過了系統限制
二、vfork函式
vfork函式的呼叫序列和返回值與fork相同。但兩者的語義不同。
vfork用於建立一個新程式,而該新程式的目的是exec一個新程式。vfork與fork都建立一 個子程式,但它不將父程式的地址空間複製到子程式中,因為子程式會立即呼叫 exec,於是不會存訪問該地址空間。相反,在子程式呼叫exec或exit之前,它在父程式的空間中執行,也就是說會更改父程式的資料段、棧和堆。vfork和fork另一區別在於 :vfork保證子程式先執行,在它呼叫exec或(exit)之後父程式才可能被排程執行。
演示vfork函式:
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
int g_val = 0;
void fun()
{
printf("child exit\n");
}
int main()
{
int val = 0;
pid_t id = vfork();
if(id < 0)
{
exit(1);
}
else if(id == 0) //child
{
atexit(fun);
printf("this is child process.\n");
++g_val;
++val;
sleep(3);
exit(0);
}
else
{
printf("this is father process\n");
printf("father exit, g_val = %d, val = %d\n", g_val, val);
}
return 0;
}
執行結果:
由執行結果可以看出,子程式直接改變了父程式的變數值,因為子程式在父程式的地址空間中執行。
相關文章
- fork()與vfork()函式函式
- 程式中fork和vfork的區別
- 理解 pcntl_fork 函式函式
- 研究linux函式 之 fork()Linux函式
- 作業系統---之fork()函式作業系統函式
- 【多程式】Linux中fork()函式詳解|多程式Linux函式
- Linux vfork()Linux
- fork函式的學習--深入瞭解計算機系統函式計算機
- Python函式與lambda 表示式(匿名函式)Python函式
- Oracle分析函式與視窗函式Oracle函式
- 建構函式與解構函式函式
- 函式節流與函式防抖函式
- 回撥函式 與 函式閉包函式
- 何時使用函式表示式與函式宣告函式
- echo與函式函式
- webgl內建函式--幾何函式與矩陣函式Web函式矩陣
- webgl內建函式--向量函式與紋理查詢函式Web函式
- 函式與極限 第一節 對映與函式函式
- 函式外與函式內的變數函式變數
- 普通函式與函式模板呼叫規則函式
- 箭頭函式與普通函式區別函式
- python內建函式-eval()函式與exec()函式的區別Python函式
- 生成函式與多項式函式
- 函式遞迴與生成式函式遞迴
- 函式宣告與函式表示式有什麼區別?函式
- 建構函式與普通函式的區別函式
- C++中函式指標與函式物件C++函式指標物件
- 箭頭函式與普通函式的區別函式
- 15.3 極限函式與和函式性質函式
- 普通函式與函式模板呼叫規則2函式
- SUBSTRING() 與 CONV() 函式函式
- JavaScript | 函式與方法JavaScript函式
- insert()與substr()函式函式
- 函式正規化入門(惰性求值與函式式狀態)函式
- JavaScript的迭代函式與迭代函式的實現JavaScript函式
- 深入理解函式節流與函式防抖函式
- 第 8 節:函式-函式型別與作用域函式型別
- fill函式與memset函式的區別(c++)函式C++
- PHP筆記:建構函式與解構函式PHP筆記函式