一、進程創(chuàng)建
fork函數(shù)初識
在Linux中,fork函數(shù)是非常重要的函數(shù),它從已存在進程中創(chuàng)建一個新進程。新進程為子進程,而原進程為父進程。
返回值:
在子進程中返回0,父進程中返回子進程的PID,子進程創(chuàng)建失敗返回-1。
進程調用fork,當控制轉移到內核中的fork代碼后,內核做:
- 分配新的內存塊和內核數(shù)據(jù)結構給子進程。
- 將父進程部分數(shù)據(jù)結構內容拷貝至子進程。
- 添加子進程到系統(tǒng)進程列表當中。
- fork返回,開始調度器調度。
fork之后,父子進程代碼共享。例如:
運行結果如下:
這里可以看到,Before只輸出了一次,而After輸出了兩次。其中,Before是由父進程打印的,而調用fork函數(shù)之后打印的兩個After,則分別由父進程和子進程兩個進程執(zhí)行。也就是說,fork之前父進程獨立執(zhí)行,而fork之后父子兩個執(zhí)行流分別執(zhí)行。
注意: fork之后,父進程和子進程誰先執(zhí)行完全由調度器決定。
fork函數(shù)返回值
fork函數(shù)為什么要給子進程返回0,給父進程返回子進程的PID?
一個父進程可以創(chuàng)建多個子進程,而一個子進程只能有一個父進程。因此,對于子進程來說,父進程是不需要被標識的;而對于父進程來說,子進程是需要被標識的,因為父進程創(chuàng)建子進程的目的是讓其執(zhí)行任務的,父進程只有知道了子進程的PID才能很好的對該子進程指派任務。
為什么fork函數(shù)有兩個返回值?
父進程調用fork函數(shù)后,為了創(chuàng)建子進程,fork函數(shù)內部將會進行一系列操作,包括創(chuàng)建子進程的進程控制塊、創(chuàng)建子進程的進程地址空間、創(chuàng)建子進程對應的頁表等等。子進程創(chuàng)建完畢后,操作系統(tǒng)還需要將子進程的進程控制塊添加到系統(tǒng)進程列表當中,此時子進程便創(chuàng)建完畢了。
也就是說,在fork函數(shù)內部執(zhí)行return語句之前,子進程就已經(jīng)創(chuàng)建完畢了,那么之后的return語句不僅父進程需要執(zhí)行,子進程也同樣需要執(zhí)行,這就是fork函數(shù)有兩個返回值的原因。
寫時拷貝
當子進程剛剛被創(chuàng)建時,子進程和父進程的數(shù)據(jù)和代碼是共享的,即父子進程的代碼和數(shù)據(jù)通過頁表映射到物理內存的同一塊空間。只有當父進程或子進程需要修改數(shù)據(jù)時,才將父進程的數(shù)據(jù)在內存當中拷貝一份,然后再進行修改。
這種在需要進行數(shù)據(jù)修改時再進行拷貝的技術,稱為寫時拷貝技術。
1、為什么數(shù)據(jù)要進行寫時拷貝?
進程具有獨立性。多進程運行,需要獨享各種資源,多進程運行期間互不干擾,不能讓子進程的修改影響到父進程。
2、為什么不在創(chuàng)建子進程的時候就進行數(shù)據(jù)的拷貝?
子進程不一定會使用父進程的所有數(shù)據(jù),并且在子進程不對數(shù)據(jù)進行寫入的情況下,沒有必要對數(shù)據(jù)進行拷貝,我們應該按需分配,在需要修改數(shù)據(jù)的時候再分配(延時分配),這樣可以高效的使用內存空間。
3、代碼會不會進行寫時拷貝?
90%的情況下是不會的,但這并不代表代碼不能進行寫時拷貝,例如在進行進程替換的時候,則需要進行代碼的寫時拷貝。
fork常規(guī)用法
- 一個進程希望復制自己,使子進程同時執(zhí)行不同的代碼段。例如父進程等待客戶端請求,生成子進程來處理請求。
- 一個進程要執(zhí)行一個不同的程序。例如子進程從fork返回后,調用exec函數(shù)。
fork調用失敗的原因
fork函數(shù)創(chuàng)建子進程也可能會失敗,有以下兩種情況:
- 系統(tǒng)中有太多的進程,內存空間不足,子進程創(chuàng)建失敗。
- 實際用戶的進程數(shù)超過了限制,子進程創(chuàng)建失敗。
二、進程終止
進程退出場景
進程退出只有三種情況:
- 代碼運行完畢,結果正確。
- 代碼運行完畢,結果不正確。
- 代碼異常終止(進程崩潰)。
進程退出碼
我們都知道m(xù)ain函數(shù)是代碼的入口,但實際上main函數(shù)只是用戶級別代碼的入口,main函數(shù)也是被其他函數(shù)調用的,例如在VS2013當中main函數(shù)就是被一個名為__tmainCRTStartup的函數(shù)所調用,而__tmainCRTStartup函數(shù)又是通過加載器被操作系統(tǒng)所調用的,也就是說main函數(shù)是間接性被操作系統(tǒng)所調用的。
既然main函數(shù)是間接性被操作系統(tǒng)所調用的,那么當main函數(shù)調用結束后就應該給操作系統(tǒng)返回相應的退出信息,而這個所謂的退出信息就是以退出碼的形式作為main函數(shù)的返回值返回,我們一般以0表示代碼成功執(zhí)行完畢,以非0表示代碼執(zhí)行過程中出現(xiàn)錯誤,這就是為什么我們都在main函數(shù)的最后返回0的原因。
當我們的代碼運行起來就變成了進程,當進程結束后main函數(shù)的返回值實際上就是該進程的進程退出碼,我們可以使用echo $?命令查看最近一次進程退出的退出碼信息。
例如,對于下面這個簡單的代碼:
代碼運行結束后,我們可以查看該進程的進程退出碼。
[cl@VM-0-15-centos procTermination]$ echo $?
這時便可以確定main函數(shù)是順利執(zhí)行完畢了。
為什么以0表示代碼執(zhí)行成功,以非0表示代碼執(zhí)行錯誤?
因為代碼執(zhí)行成功只有一種情況,成功了就是成功了,而代碼執(zhí)行錯誤卻有多種原因,例如內存空間不足、非法訪問以及棧溢出等等,我們就可以用這些非0的數(shù)字分別表示代碼執(zhí)行錯誤的原因。
C語言當中的strerror函數(shù)可以通過錯誤碼,獲取該錯誤碼在C語言當中對應的錯誤信息:
運行代碼后我們就可以看到各個錯誤碼所對應的錯誤信息:
實際上Linux中的ls、pwd等命令都是可執(zhí)行程序,使用這些命令后我們也可以查看其對應的退出碼。
可以看到,這些命令成功執(zhí)行后,其退出碼也是0。
但是命令執(zhí)行錯誤后,其退出碼就是非0的數(shù)字,該數(shù)字具體代表某一錯誤信息。
注意: 退出碼都有對應的字符串含義,幫助用戶確認執(zhí)行失敗的原因,而這些退出碼具體代表什么含義是人為規(guī)定的,不同環(huán)境下相同的退出碼的字符串含義可能不同。
進程正常退出
return退出
在main函數(shù)中使用return退出進程是我們常用的方法。
例如,在main函數(shù)最后使用return退出進程。
運行結果:
exit函數(shù)
使用exit函數(shù)退出進程也是我們常用的方法,exit函數(shù)可以在代碼中的任何地方退出進程,并且exit函數(shù)在退出進程前會做一系列工作:
- 執(zhí)行用戶通過atexit或on_exit定義的清理函數(shù)。
- 關閉所有打開的流,所有的緩存數(shù)據(jù)均被寫入。
- 調用_exit函數(shù)終止進程。
例如,以下代碼中exit終止進程前會將緩沖區(qū)當中的數(shù)據(jù)輸出。
運行結果:
_exit函數(shù)
使用_exit函數(shù)退出進程的方法我們并不經(jīng)常使用,_exit函數(shù)也可以在代碼中的任何地方退出進程,但是_exit函數(shù)會直接終止進程,并不會在退出進程前會做任何收尾工作。
例如,以下代碼中使用_exit終止進程,則緩沖區(qū)當中的數(shù)據(jù)將不會被輸出。
運行結果:
return、exit和_exit之間的區(qū)別與聯(lián)系
return、exit和_exit之間的區(qū)別
只有在main函數(shù)當中的return才能起到退出進程的作用,子函數(shù)當中return不能退出進程,而exit函數(shù)和_exit函數(shù)在代碼中的任何地方使用都可以起到退出進程的作用。
使用exit函數(shù)退出進程前,exit函數(shù)會執(zhí)行用戶定義的清理函數(shù)、沖刷緩沖,關閉流等操作,然后再終止進程,而_exit函數(shù)會直接終止進程,不會做任何收尾工作。
return、exit和_exit之間的聯(lián)系
執(zhí)行return num等同于執(zhí)行exit(num),因為調用main函數(shù)運行結束后,會將main函數(shù)的返回值當做exit的參數(shù)來調用exit函數(shù)。
使用exit函數(shù)退出進程前,exit函數(shù)會先執(zhí)行用戶定義的清理函數(shù)、沖刷緩沖,關閉流等操作,然后再調用_exit函數(shù)終止進程。
進程異常退出
情況一:向進程發(fā)生信號導致進程異常退出。
例如,在進程運行過程中向進程發(fā)生kill -9信號使得進程異常退出,或是使用Ctrl+C使得進程異常退出等。
情況二:代碼錯誤導致進程運行時異常退出。
例如,代碼當中存在野指針問題使得進程運行時異常退出,或是出現(xiàn)除0的情況使得進程運行時異常退出等。
-
Linux
+關注
關注
87文章
11432瀏覽量
212479 -
PID
+關注
關注
37文章
1479瀏覽量
87075 -
函數(shù)
+關注
關注
3文章
4365瀏覽量
63953 -
數(shù)據(jù)結構
+關注
關注
3文章
573瀏覽量
40542 -
Fork
+關注
關注
0文章
14瀏覽量
3430
發(fā)布評論請先 登錄
Linux中進程和線程的深度對比

評論