man vfork:
NAME
vfork - create a child process and block parent
SYNOPSIS
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
DESCRIPTION
Standard description
(From POSIX.1) The vfork() function has the same effect as fork(2), except that the behav‐
ior is undefined if the process created by vfork() either modifies any data other than a
variable of type pid_t used to store the return value from vfork(), or returns from the
function in which vfork() was called, or calls any other function before successfully
calling _exit(2) or one of the exec(3) family of functions.
Linux description
vfork(), just like fork(2), creates a child process of the calling process. For details
and return value and errors, see fork(2).
vfork() is a special case of clone(2). It is used to create new processes without copying
the page tables of the parent process. It may be useful in performance-sensitive applica‐
tions where a child is created which then immediately issues an execve(2).
vfork() differs from fork(2) in that the calling thread is suspended until the child ter‐
minates (either normally, by calling _exit(2), or abnormally, after delivery of a fatal
signal), or it makes a call to execve(2). Until that point, the child shares all memory
with its parent, including the stack. The child must not return from the current function
or call exit(3), but may call _exit(2).
As with fork(2), the child process created by vfork() inherits copies of various of the
caller's process attributes (e.g., file descriptors, signal dispositions, and current
working directory); the vfork() call differs only in the treatment of the virtual address
space, as described above.
Signals sent to the parent arrive after the child releases the parent's memory (i.e.,
after the child terminates or calls execve(2)).
建立一個新程序的方法隻有由某個已存在的程序調用fork()或vfork(),當然某些程序如init等是作為系統啟動的一部風而被核心建立的。
一、fork
1. 調用方法
pid_t fork(void);
正确傳回:在父程序中傳回子程序的程序号,在子程序中傳回0
錯誤傳回:-1
子程序是父程序的一個拷貝。即,子程序從父程序得到了資料段和堆棧段的拷貝,這些需要配置設定新的記憶體;而對于隻讀的代碼段,通常使用共享記憶體的方式通路。fork傳回後,子程序和父程序都從調用fork函數傳回處開始執行。
父程序與子程序的不同之處在于:fork的傳回值不同——父程序中的傳回值為子程序的程序号,而子程序為0
2. fork函數調用的用途
⑴ 一個程序希望複制自身,進而父子程序能同時執行不同段的代碼。
⑵ 程序想執行另外一個程式
二、vfork
與fork函數完全相同
2. vfork函數調用的用途
用vfork建立的程序主要目的是用exec函數執行另外的程式,與fork的第二個用途相同
三、fork與vfork的差別
1. fork要拷貝父程序的資料段;而vfork則不需要完全拷貝父程序的資料段,在子程序沒有調用exec和exit之前,子程序與父程序共享資料段
2. fork不對父子程序的執行次序進行任何限制;而在vfork調用中,子程序先運作,父程序挂起,直到子程序調用了exec或exit之後,父子程序的執行次序才不再有限制
四、結束子程序
結束子程序不用exit(0),而使用_exit(0)。這是因為_exit(0)在結束程序時,不對标準I/O流進行任何操作。而exit(0)則會關閉程序的所有标準I/O流。
由于子程序與父程序的運作是無關的,父程序可先于子程序運作,子程序也可先于父程序運作,是以下段程式可以有兩種運作結果。
root@iZ23onhpqvwZ:~/ms/linux/unp/unpMy/tcpcliserv# ./fork1
before fork
parent did nog changed
global=4,vari=5
root@iZ23onhpqvwZ:~/ms/linux/unp/unpMy/tcpcliserv# child changed
global=5,vari=4
root@happy src]# ./a.out
Child changed
globa = 5 vari = 4
Parent did not changde
globa = 4 vari = 5
vfork建立新程序的主要目的在于用exec函數執行另外的程式,實際上,在沒調用exec或exit之前子程序的運作中是與父程序共享資料段的。在vfork調用中,子程序先運作,父程序挂起,直到子程序調用exec或exit,在這以後,父子程序的執行順序不再有限制。
root@iZ23onhpqvwZ:~/ms/linux/unp/unpMy/tcpcliserv# ./fork2
child changed
global=5,vari=4。
(從這裡可以看出共享了資料段的。)
vfork
保證子程序先運作,在她調用
exec
或
exit
之後父程序才可能被排程運作。如果在
調用這兩個函數之前子程序依賴于父程序的進一步動作,則會導緻死鎖。
ubuntu輸出:
root@iZ23onhpqvwZ:~/ms/linux/unp/unpMy/tcpcliserv# ./vfork1
count=1
count=2
vfork1: cxa_atexit.c:100: __new_exitfn: Assertion `l != ((void *)0)' failed.
Aborted
有return 0,輸出:
........................
如果沒有_exit(0)的話,子程序沒有調用exec或exit,是以父程序是不可能執行的,在子程序調用exec或exit之後父程序才可能被排程運作。 是以我們加上_exit(0);使得子程序退出,父程序執行,這樣else後的語句就會被父程序執行,又因在子程序調用exec或exit之前與父程序資料是共享的,是以子程序退出後把父程序的資料段count改成1了,子程序退出後,父程序又執行,最終就将count變成了2,
改成:
網上抄的一段,可以再了解了解:
為什麼會有vfork,因為以前的fork很傻,當它建立一個子程序時,将會建立一個新的位址空間,并且拷貝父程序的資源,而往往在子程序中會執行exec調用,這樣,前面的拷貝工作就是白費力氣了,這種情況下,聰明的人就想出了vfork,它産生的子程序剛開始暫時與父程序共享位址空間(其實就是線程的概念了),因為這時候子程序在父程序的位址空間中運作,是以子程序不能進行寫操作,并且在兒子“ 霸占”着老子的房子時候,要委屈老子一下了,讓他在外面歇着(阻塞),一旦兒子執行了exec或者exit後,相當于兒子買了自己的房子了,這時候就相當于分家了。
linux c 下exit(0);與_exit(0);的差別
調試後你會發現沒有
hello word
exit是退出去先把記憶體中的資料輸出到檔案中,而_exit 這個直接退出,消除記憶體中的資料;
printf是标準行輸出,遇到“\n”或者是寫入的記憶體滿了才會标準輸出;
我們可以嘗試在hello word 中加入很多i,假設輸入2000個吧(關于行輸出自行google),再次調試發現,會有 hello 等字元,這就是溢出了。
回到前面,為什麼開始沒有hello,雖然hello word 在_exit前,但是你檢視彙編會發現,他隻是講資料存在記憶體中。沒有講資料真正輸出。當我們把_exit去掉 hello word就能顯示了。為什麼呢?這個就是編譯器自己加的了。
從圖中可以看出,_exit 函數的作用是:直接使程序停止運作,清除其使用的記憶體空間,并清除其在核心的各種資料結構;exit 函數則在這些基礎上做了一些小動作,在執行退出之前還加了若幹道工序。exit() 函數與 _exit() 函數的最大差別在于exit()函數在調用exit 系統調用前要檢查檔案的打開情況,把檔案緩沖區中的内容寫回檔案。也就是圖中的“清理I/O緩沖”。
所需頭檔案: exit: #include<stdlib.h>
_exit: #include<unistd.h>
函數原型:exit: void exit(int status)
_exit: void _exit(int status)
函數傳入值:status 是一個整型的參數,可以利用這個參數傳遞程序結束時的狀态。一般來說,0表示正常結束;其他的數值表示出現了錯誤,程序非正常結束。在實際程式設計時,父程序可以利用wait 系統調用接收子程序的傳回值,進而針對不同的情況進行不同的處理
atexit()函數
atexit可以注冊終止處理程式,ANSI C規定最多可以注冊32個終止處理程式。
終止處理程式的調用與注冊次序相反
#include <stdlib.h>
int atexit(void (*function)(void));
程序的終止方式:
有8種方式使程序終止,其中前5種為正常終止,它們是
1:從 main 傳回
2:調用 exit
3:調用 _exit 或 _Exit
4:最後一個線程從其啟動例程傳回
5:最後一個線程調用pthread_exit
異常終止有3種,它們是
6:調用 abort
7:接到一個信号并終止
8:最後一個線程對取消請求做出響應