讓我們對這段關(guān)于linux進程創(chuàng)建和終止的文章進行偽原創(chuàng)處理,同時保持原意不變,并保留圖片的原始位置和格式:
- 進程創(chuàng)建語言:JavaScript運行次數(shù):0運行復(fù)制
#include <unistd.h> pid_t fork(void);
返回值:子進程返回0,父進程返回子進程ID,錯誤時返回-1
當(dāng)進程調(diào)用fork函數(shù)時,控制權(quán)會轉(zhuǎn)移到內(nèi)核中的fork代碼。內(nèi)核會執(zhí)行以下操作:
- 為子進程分配新的內(nèi)存塊和內(nèi)核數(shù)據(jù)結(jié)構(gòu)
- 將父進程的部分?jǐn)?shù)據(jù)結(jié)構(gòu)內(nèi)容復(fù)制到子進程
- 將子進程添加到系統(tǒng)進程列表中
- fork返回后,開始調(diào)度器的調(diào)度
寫時拷貝
02. 進程終止首先需要明確,終止的目的是什么:
- 釋放曾經(jīng)占用的代碼和數(shù)據(jù)空間
- 釋放內(nèi)核數(shù)據(jù)結(jié)構(gòu)
進程退出場景:
- 代碼運行完畢,結(jié)果正確
- 代碼運行完畢,結(jié)果不正確(可以通過進程退出碼判斷這兩種情況)
- 代碼異常終止
上面的代碼中,進程11258是父進程bash,執(zhí)行echo $?命令,父進程bash可以獲取最近一個子進程的退出碼。我們之前提到,echo是一個內(nèi)建命令,打印的是bash內(nèi)部的變量數(shù)據(jù)。
父進程bash為什么需要獲取子進程的退出碼呢?這是為了了解子進程的退出情況(成功,失敗,以及失敗的原因)。
進程結(jié)束時,可以通過return語句(在函數(shù)中)或exit()函數(shù)(直接從程序中)指定一個退出碼。這個退出碼是一個整數(shù),傳遞給父進程,用于表示子進程的終止?fàn)顟B(tài)。
常見的慣例:
- 0(EXIT_SUCCESS):通常表示成功。程序執(zhí)行完成且沒有錯誤。
- 非0(EXIT_FaiLURE):通常表示有錯誤發(fā)生。具體的非零值可以用來指示不同類型的錯誤。
異常終止在操作系統(tǒng)中,進程的異常終止通常是由于一些錯誤或意外情況導(dǎo)致程序無法正常運行到結(jié)束。以下是一些典型的異常終止情況:
- 程序錯誤
- 資源問題
- 內(nèi)存耗盡:程序請求更多內(nèi)存時,如果系統(tǒng)無法分配(如堆內(nèi)存耗盡),可能會導(dǎo)致程序異常終止。
- 文件描述符耗盡:程序打開太多文件而沒有關(guān)閉,達到系統(tǒng)限制,可能導(dǎo)致系統(tǒng)函數(shù)失敗,影響程序繼續(xù)運行。
- 信號
- 致命信號:
- SigsEGV(段錯誤信號):最常見的程序崩潰原因,通常是由于訪問違法的內(nèi)存地址。
- SIGABRT(中止信號):通常是由于程序內(nèi)部發(fā)生嚴(yán)重錯誤或調(diào)用 abort() 函數(shù)而觸發(fā)。
- SIGFPE(浮點異常信號):執(zhí)行了一個無效的算術(shù)運算,比如除以零。
- SIGKILL:無條件終止程序運行的信號,無法捕獲或忽略。
- SIGTERM:請求終止程序的信號,比 SIGKILL 更溫和,允許程序進行清理(關(guān)閉文件、釋放資源等)操作后退出。
- 非致命信號(如 SIGINT、SIGHUP 等),如果沒有被程序正確處理,也可能導(dǎo)致程序終止。
- 致命信號:
- 操作系統(tǒng)干預(yù)
- 死鎖檢測:操作系統(tǒng)可能終止處于死鎖狀態(tài)的進程以解鎖系統(tǒng)資源。
- 資源超額:操作系統(tǒng)對程序使用的資源(如 CPU 時間、內(nèi)存使用量)有限制,如果程序超出這些限制,如超過了設(shè)定的 CPU 時間,操作系統(tǒng)可能終止這個進程。
- 運行時異常
- 未捕獲的異常:在一些高級語言中(如 Java、python),如果程序中發(fā)生了異常而沒有被捕獲和處理,這通常會導(dǎo)致程序異常終止。例如,Python 中未被捕獲的 ValueError 或 IndexError。
一旦出現(xiàn)異常,退出碼就沒有意義了!進程出異常,本質(zhì)是因為進程收到了操作系統(tǒng)發(fā)給進程的信號!
段錯誤,操作系統(tǒng)提前終止進程
我們可以通過查看進程退出的信號來判斷進程為什么會異常!
首先要確認(rèn)是否是異常,不是異常的情況下,進程一定是運行完畢了,這時只需查看退出碼即可。衡量一個進程的退出情況,我們只需要兩個數(shù)字:退出碼和退出信號!
如何終止進程?正常退出:
- main函數(shù)return,表示進程終止(非main函數(shù)的return,僅表示函數(shù)結(jié)束)
- 代碼任意位置調(diào)用exit函數(shù),注意:exit函數(shù)會導(dǎo)致進程立即退出
- _exit(),系統(tǒng)調(diào)用
異常退出:
- 通過Ctrl + C觸發(fā)信號終止
在unix和類Unix系統(tǒng)中,_exit()和exit()都用于終止進程,但它們在功能和使用場景上有重要的區(qū)別。理解這些區(qū)別有助于正確地管理程序的終止過程,特別是在涉及資源清理和子進程管理時。
exit()函數(shù)由C標(biāo)準(zhǔn)庫提供,用于結(jié)束程序。它執(zhí)行幾個重要的清理操作,然后調(diào)用底層的_exit()或exit_group()系統(tǒng)調(diào)用來終止進程。
特點和操作:
- 刷新緩沖區(qū):exit()會自動刷新所有stdio的緩沖區(qū),將緩沖區(qū)內(nèi)的數(shù)據(jù)寫入文件。這確保了所有掛起的輸出(例如,使用printf()產(chǎn)生的輸出)都被正確地寫出。
- 執(zhí)行atexit()注冊的函數(shù):如果程序中使用了atexit()注冊了任何終止時執(zhí)行的函數(shù),exit()會在實際終止進程前按注冊的逆序調(diào)用這些函數(shù)。這可以用于執(zhí)行一些如關(guān)閉文件描述符、釋放分配的內(nèi)存等清理工作。
- 關(guān)閉stdio庫:關(guān)閉所有使用標(biāo)準(zhǔn)I/O庫打開的文件等資源。
使用場景主要是普通的應(yīng)用程序,在需要確保輸出數(shù)據(jù)完整性和執(zhí)行特定的清理操作時使用。
_exit()函數(shù)由POSIX標(biāo)準(zhǔn)指定,直接調(diào)用系統(tǒng)級別的退出操作,用于立即結(jié)束程序,不執(zhí)行標(biāo)準(zhǔn)I/O的清理操作和不調(diào)用atexit()或者c++的全局對象的析構(gòu)函數(shù)。
特點和操作:
- 不刷新緩沖區(qū):不處理stdio的緩沖區(qū),如果緩沖區(qū)內(nèi)有未寫入的數(shù)據(jù),這些數(shù)據(jù)將丟失。
- 不執(zhí)行atexit()注冊的函數(shù):任何通過atexit()注冊的函數(shù)都不會被執(zhí)行。
- 立即終止:提供一種確保程序能迅速終止的方式,通常用于子進程退出或者在錯誤處理中需要立即終止程序時使用。
使用場景主要是在創(chuàng)建子進程后,子進程完成任務(wù)立即退出時,或者在程序遇到無法恢復(fù)的錯誤需要立即終止時使用。
- 使用exit()當(dāng)你需要正常終止程序,并且需要清理資源(如關(guān)閉文件、保存狀態(tài)等)。
- 使用_exit()在需要快速退出且不關(guān)心資源清理的場景下,比如在子進程中執(zhí)行了某個任務(wù)后,或者在出現(xiàn)嚴(yán)重錯誤時安全退出。
選擇合適的函數(shù)可以避免數(shù)據(jù)丟失和資源泄漏,確保程序的穩(wěn)定和安全。
return是一種更常見的退出進程方法。執(zhí)行return n等同于執(zhí)行exit(n),因為調(diào)用main的運行時函數(shù)會將main的返回值當(dāng)做exit的參數(shù)。