【Linux庖丁解牛】——進程等待!

1. 進程退出場景

進程退出一般有三種場景:

。代碼運行完畢,結果正確

。代碼運行完畢,結果錯誤【比如,我們要對某個文件進行寫入,但寫入的文件路徑出錯,代碼運行完畢,可是結果出錯】

。代碼異常終止【這種情況的原因有很多種,我們在寫C/C++代碼時肯定遇到過】

一般進程的退出結果是交給父進程的,因為父進程創建子進程是為了讓子進程幫父進程完成某種任務,所以父進程肯定需要拿到子進程的退出結果。

那父進程是如何拿到子進程的退出結果呢?我們下面慢慢說。

2. 進程常見退出方法

2.1 從main函數返回

我們以往在寫C/C++程序時,通常會在main函數的結尾返回0,但至于這個返回值的作用是什么,我們不知道,并且我們也不關心!但是,我們現在有必要知道了:

main函數的返回值通常表明你程序的執行情況!

。return 0;表明代碼運行完畢,結果正確

。return 非0;表面代碼運行完畢,結果錯誤【不同的值表明不同的出錯原因】

下面,我們來寫一段代碼來見一見main函數的不同返回值:

以上代碼,我們打開一個文件進行讀的操作,但是在該目錄下,我并沒有這個文件,所以肯定是打開失敗的。所以函數會返回1。?

但是,我們怎么知道這個進程的退出結果呢?

這里補充一個概念:退出碼【退出碼(退出狀態)可以告訴我們最后?次執行的命令的狀態。在命令結束以后,我們可以知道命令 是成功完成的還是以錯誤結束的。其基本思想是,程序返回退出代碼0 時表示執行成功,沒有問題。 代碼1 或0 以外的任何代碼都被視為不成功

每個進程退出后進入僵尸狀態,其退出碼都會寫入自己的task_struct中【exit_code】。

我們只要在命令行上敲下以下指令就可以查看到最近一個進程退出時的退出碼:

其實,在C標準中,規定了每個退出碼都有自己對應的錯誤信息,strerror就可以把退出碼轉化成對應的錯誤信息!

下面我們就來看一下C標準規定了多少錯誤信息:

可以看到從0開始一共有134個退出碼!

最后,如果進程異常終止,退出碼就不重要了,就好比你考試作弊被發現了,你的結果也就不重要了。

2.2 exit函數

其實除了從main中用return返回結束進程,我們還可以用exit函數結束進程。

并且在程序的任何地方調用該函數,進程都會直接退出并返回退出碼給父進程!

?只打印“fun begin”驗證了這一點!

2.3 _exit函數

如果exit退出的時候,進程退出的時候,會進行緩沖區刷新。

如果_exit退出的時候,進程退出的時候,會進行緩沖區刷新。

以上這一點本身并不重要,了解即可。

我們知道庫函數和系統調用是上下級關系,即exit的底層還是_exit,但為什么_exit卻不會刷新緩沖區呢?

因為緩沖區是庫級別的【C語言提供的】,也就是庫緩沖區,這里只點出來,更多細節,后面再說。

3. 進程等待

3.1 為什么要有進程等待

? 之前講過,子進程退出,父進程如果不管不顧,就可能造成‘僵尸進程’的問題,進而造成內存泄漏。

? 另外,進程?旦變成僵尸狀態,那就刀槍不入,“殺?不眨眼”的kill-9也無能為力,因為誰也沒有辦法殺死?個已經死去的進程。

? 最后,父進程派給子進程的任務完成的如何,我們需要知道。如,子進程運行完成,結果對還是 不對,或者是否正常退出。

? 父進程通過進程等待的方式,回收子進程資源獲取子進程退出信息

3.2 進程等待是什么

要搞清楚這個問題,我們先來見一見進程等待先簡單使用一下!

a. wait方法

wait其中的參數wstatus其實是輸出型參數,這點我會在waitpid處再詳細說明。?

這里我們先使用wait方法解決僵尸進程的問題:

以下代碼我先創建了一個子進程,在子進程運行3秒后退出,而父進程先休眠5秒后,再使用wait等待子進程,最后讓父進程運行10秒后退出。這樣做的原因是方便我們檢測時,先看到子進程的僵尸狀態,父進程等待成功后,我們可以看到子進程從僵尸狀態被回收的過程

  1#include <stdio.h>2 #include <sys/types.h>3 #include <unistd.h>4 #include <wait.h>5 #include <stdlib.h>6 7 int main()8 {9     pid_t id=fork();//創建子進程10     if(id==0)//子進程11     {12         int cnt=3;13         while(cnt--)14         {15             printf("子進程pid->%d\n",getpid());16             sleep(1);17         }18         exit(0);19     }20     //父進程等待子進程21     sleep(5);//父進程先等待5秒,可以看到子進程的僵尸狀態22     int exit_code=0;                                                                                                                            23     pid_t rid=wait(&exit_code);24     if(rid>0) printf("等待子進程成功!rid->%d\n",rid);25     sleep(10);26     return 0;27 }

運行結果:

下面是檢測寫的簡單shell腳本:?

while :; do ps axj | head -1 && ps axj | grep code; sleep 1;done

檢測結果:

結果也確實和我們預期的一樣!

這里還有兩個細節 ,如果父進程在等待子進程的中,子進程沒有退出,那么父進程就在wait處等待【可以理解為scanf】!父進程wait時會等待它任意一個子進程退出并獲得其pid。

b. waitpid方法

waitpid有3個參數,其中第一個參數有以下四種填寫方法,目前我們先不管<-1和=0,-1表明等待任意子進程退出,>0則表明等待指定子進程退出。?

?第二個參數為輸出型參數,我們之前便說過,父進程創建子進程是為了讓子進程幫父進程完成某些任務。所以父進程應該有能力知道【不管它想不想知道】子進程的退出結果【即退出信息】,而第二個指針參數就是為了做到這一點而存在的,而wstatus獲得的正好就是子進程的退出碼!

第三個參數可以控制阻塞【我們先暫時不管】

下面我們來看看waitpid到底能不能獲得子進程的退出碼:

按照我們的預期,exit_code應該是1!

但是結果似乎和我們預期的不太一樣!

當實際情況和預期不一樣時,那一定是我們的認知出了問題!

這里就直說了,整形的exit_code接收退出碼時,只用了32個比特位中的8~15這8個比特位來記錄退出碼【我們可以通過位圖來理解】!而16~32這16個比特位沒有被使用,至于1~8這8個比特位在子進程正常退出時都默認是0,而只有在子進程異常退出時,這八個比特位才會被使用,這和信號有關,細節我們以后再說!

既然如此,那我們拿到記錄退出碼的那八個比特位就可以看到正確的退出碼了!

修改這一句代碼即可!

if(rid>0) printf("等待子進程成功!rid->%d,exit_code->%d\n",rid,(exit_code>>8)&0xFF);

還有一個問題,我們為何不定義一個全局變量來記錄子進程的退出碼呢,在子進程退出時,修改這個變量!原因也很簡單,你子進程修改它,發生寫時拷貝,父進程和子進程看到的全局變量根本就不是同一個!

最后,如果進程異常退出,退出碼就沒有意義了,但是,我們還是可以通過waitpid的輸出型參數拿到退出信號。

稍微修改一下代碼,做一個實驗:

手動殺掉進程!父進程得到相應的退出信號!

?我們可以通過指令kill -l來查找所有的信號:

這里只是簡單的見了見信號,具體細節還沒有說!

?3.3 進程等待怎么辦到

現在,我們理解了什么是進程等待,為什么要有進程等待,但是我們還是不知道,父進程通過進程等待是如何拿到子進程的退出碼和退出信號的!

要明白這個問題,我們首先需要知道這些信息存放到哪里!我們都知道一個進程退出后,它的進程虛擬空間 | 頁表 | 代碼和數據 都會被系統回收和釋放!但是,他的PCB【task_struct】卻會被保存起來,進入僵尸狀態!所以,子進程的退出碼和退出信號勢必在它的PCB中!所以,進程退出時,它的退出信息會被記錄進它的PCB中的某些變量中!

事實也是如此:在源碼中記錄信息的變量如下所示

所以,父進程通過系統調用的方式讓系統幫它去子進程的PCB中拿到子進程的退出信息返回給父進程。至此,我們也就明白了,進程等待的原理了。而且,我們現在也更加明白了,為什么進程要有僵尸狀態【1. 為了方便父進程回收子進程 2. 為了父進程方便拿到子進程的退出信息

3.4 阻塞與非阻塞等待

上面我們提到過waitpid的第三個參數使用來控制阻塞和非阻塞調用的,在默認情況下【不填參數】,waitpid進行的是阻塞調用,什么是阻塞調用呢?

阻塞調用:父進程在waitpid處等待子進程退出,如果子進程一直不退出,那父進程則會一直等待,并且只做這一件事情!

如果,我們將參數填寫為WNOHANG,則為非阻塞調用,非阻塞調用:父進程每隔一段時間去看一看子進程是否退出,如果退出,則返回值大于0,如果調用結束,子進程沒有退出,則返回0,如果返回值小于0,則調用失敗。在子進程未退出之前,父進程可以利用其他時間做一些其他的事情,所以非阻塞調用一般效率更高【并不是指子進程退出的效率高】。

如何做到非阻塞等待,其實也很簡單,我僅們需要做一次非阻塞輪詢,說白了,就是循環!

下面一個例子就很好的體現了非阻塞調用下父進程利用等待時間完成其他的任務。?

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>
typedef void (*handler_t)(); // 函數指針類型 
std::vector<handler_t> handlers; // 函數指針數組 
void fun_one() {printf("這是?個臨時任務1\n");
}
void fun_two() {printf("這是?個臨時任務2\n");
}
void Load() {handlers.push_back(fun_one);handlers.push_back(fun_two);
}
void handler() {if (handlers.empty())Load();for (auto iter : handlers)iter();
}
int main() {pid_t pid;pid = fork();if (pid < 0) {printf("%s fork error\n", __FUNCTION__);return 1;}else if (pid == 0) { // childprintf("child is run, pid is : %d\n", getpid());sleep(5);exit(1);}else {int status = 0;pid_t ret = 0;do {ret = waitpid(-1, &status, WNOHANG); // ?阻塞式等待 if (ret == 0) {printf("child is running\n");}handler();} while (ret == 0);if (WIFEXITED(status) && ret == pid) {printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));}else {printf("wait child failed, return.\n");return 1;}}return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/83939.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/83939.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/83939.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

鴻蒙OSUniApp 制作簡潔高效的標簽云組件#三方框架 #Uniapp

UniApp 制作簡潔高效的標簽云組件 在移動端應用中&#xff0c;標簽云&#xff08;Tag Cloud&#xff09;是一種常見的UI組件&#xff0c;它以視覺化的方式展示關鍵詞或分類&#xff0c;幫助用戶快速瀏覽和選擇感興趣的內容。本文將詳細講解如何在UniApp框架中實現一個簡潔高效的…

ubuntu14.04/16.06 安裝vscode(實測可以用)

地址&#xff1a;https://code.visualstudio.com/updates/v1_38 選擇deb 這個版本還支持ubuntu14.04和16.06 sudo dpkg -i code_1.38.1-1568209190_amd64.deb sudo apt-get install -f安裝成功&#xff0c;正常使用

WebRTC技術EasyRTC音視頻實時通話驅動智能攝像頭邁向多場景應用

一、方案背景? 在物聯網蓬勃發展的當下&#xff0c;智能攝像頭廣泛應用于安防、家居、工業等領域。但傳統智能攝像頭存在視頻傳輸延遲高、設備兼容性差、網絡波動時傳輸不穩定等問題&#xff0c;難以滿足用戶對實時流暢交互視頻的需求。EasyRTC憑借低延遲、高可靠、跨平臺特性…

Java EE進階1:導讀

1.發展歷程 2.學習內容 前?的課程中,學習的是Java基礎,JavaEE主要學習Java的應用,也就是學習Java在企業中是如何應用的 Java更多場景是業務開發,更狹義點可以理解為web開發.所以咱們的學習也是圍繞著如何使用Java來做web開發 2.1 什么是Web開發&#xff1f; web&#xff08…

APPtrace 智能參數系統:重構 App 用戶增長與運營邏輯

一、免填時代&#xff1a;APPtrace 顛覆傳統參數傳遞模式 傳統 App 依賴「邀請碼 / 手動綁定」實現用戶關聯&#xff0c;流程繁瑣導致 20%-30% 的用戶流失。APPtrace 通過 **「鏈接參數自動傳遞 安裝后智能識別」** 技術&#xff0c;讓用戶在無感知狀態下完成關系綁定、場景還…

bisheng系列(一)- 本地部署(Docker)

目錄 一、導讀 二、說明 1、鏡像說明 2、本節內容 三、docker部署 1、克隆代碼 2、運行鏡像 3、可能的錯誤信息 四、頁面測試 1、注冊用戶 2、登陸成功 3、添加模型 一、導讀 環境&#xff1a;Ubuntu 24.04、Windows 11、WSL 2、Python 3.10 、bisheng 1.1.1 背景…

docker介紹與常用命令匯總

docker簡介 docker是什么&#xff1f; Docker 是一個開源的應用容器引擎&#xff0c;它可以讓開發者將應用與運行環境打包成一個標準的、可移植的容器&#xff08;Container&#xff09;&#xff0c;在任何地方都可以快速部署和運行&#xff0c;無需關心底層環境是否一致。 …

Android 中拖拽從一個組件到另外一個組件的寫法(跨容器拖拽)

在 Android 中&#xff0c;拖拽一個圖片&#xff08;例如 ImageView&#xff09;到另一個組件&#xff08;如 LinearLayout、FrameLayout 等容器&#xff09;涉及以下步驟&#xff1a; 準備工作 源組件&#xff1a;你從哪里開始拖動&#xff08;如 ImageView&#xff09;。 目…

火絨互聯網安全軟件:自主引擎,精準防御

在數字時代&#xff0c;網絡安全是每一個用戶都必須重視的問題。無論是個人用戶還是企業用戶&#xff0c;都需要一款高效、可靠的反病毒軟件來保護設備免受惡意軟件的侵害。今天&#xff0c;我們要介紹的 火絨互聯網安全軟件&#xff0c;就是這樣一款由資深工程師主導研發并擁有…

使用亮數據代理IP+Python爬蟲批量爬取招聘信息訓練面試類AI智能體(手把手教學版)

文章目錄 一、為什么要用代理IP&#xff1f;(重要&#xff01;&#xff01;&#xff01;)二、環境準備&#xff08;三件套走起&#xff09;2.1 安裝必備庫&#xff08;pip大法好&#xff09;2.2 獲取亮數據代理&#xff08;官網注冊送試用&#xff09; 三、編寫爬蟲代碼&#x…

Android屏幕采集編碼打包推送RTMP技術詳解:從開發到優化與應用

在現代移動應用中&#xff0c;屏幕采集已成為一個廣泛使用的功能&#xff0c;尤其是在實時直播、視頻會議、遠程教育、游戲錄制等場景中&#xff0c;屏幕采集技術的需求不斷增長。Android 平臺為開發者提供了 MediaProjection API&#xff0c;這使得屏幕錄制和采集變得更加簡單…

互聯網大廠Java求職面試:Spring AI與大模型交互的高級模式與自定義開發

互聯網大廠Java求職面試&#xff1a;Spring AI與大模型交互的高級模式與自定義開發 在當今技術領域&#xff0c;隨著AI和大模型技術的廣泛應用&#xff0c;如何在復雜的系統架構中高效地集成這些技術成為了各大互聯網公司關注的重點。本文將通過一場模擬的面試對話&#xff0c…

MySQL 8.0 OCP 1Z0-908 161-170題

Q161.Examine this command, which executes successfully: cluster.addInstance ( ‘:’,{recoveryMethod: ‘clone’ 1}) Which three statements are true? (Choose three.) A)The account used to perform this recovery needs the BACKUP_ ADMIN privilege. B)A target i…

藍橋杯1447 砝碼稱重

問題描述 你有一架天平和 N 個砝碼&#xff0c;這 N 個砝碼重量依次是 W1,W2,???,WN?。 請你計算一共可以稱出多少種不同的重量&#xff1f; 注意砝碼可以放在天平兩邊。 輸入格式 輸入的第一行包含一個整數 N。 第二行包含 N 個整數&#xff1a;W1,W2,W3,???,WN?…

金融量化智能體,如何開發一個有效的策略?

原創內容第887篇&#xff0c;專注智能量化投資、個人成長與財富自由。 本周重構了網站&#xff0c;升級了最新的回測引擎&#xff0c;以及升級了論壇。 策略年化210%&#xff0c;夏普比3.47&#xff0c;系統源代碼及策略均可下載 年化37.5%&#xff0c;回撤控制在16.8%&…

JavaScript 性能優化:調優策略與工具使用

引言 在當今的 Web 開發領域&#xff0c;性能優化已不再是錦上添花&#xff0c;而是產品成功的關鍵因素。據 Google 研究表明&#xff0c;頁面加載時間每增加 3 秒&#xff0c;跳出率將提高 32%。而移動端用戶如果頁面加載超過 3 秒&#xff0c;有 53% 的用戶會放棄訪問。性能…

為 Jenkins添加 Windows Slave遠程執行 python項目腳本

測試環境 JAVA JDK 1.7.0_13 (jdk-7u13-windows-i586.exe) Jenkins Win11 64 python項目環境 實踐操作 1、新建與配置結點 【系統管理】-> 【管理結點】-> 【新建結點】, 如上&#xff0c;輸入結點名稱&#xff0c;勾選 【Dumb Slave】&#xff0c;點擊【OK】 說明&am…

基于springboot3 VUE3 火車訂票系統前后端分離項目適合新手學習的項目包含 智能客服 換乘算法

? 博主介紹&#xff1a;專注于Java&#xff08;springboot ssm 等開發框架&#xff09; vue .net php phython node.js uniapp 微信小程序 等諸多技術領域和畢業項目實戰、企業信息化系統建設&#xff0c;從業十五余年開發設計教學工作 ☆☆☆ 精彩專欄推薦訂閱☆☆☆☆☆…

btc交易所關鍵需求區 XBIT反彈與上漲潛力分析??

在加密貨幣市場的浪潮中&#xff0c;狗狗幣&#xff08;DOGE&#xff09;近期的走勢吸引了眾多投資者的目光。根據XBIT分析&#xff0c;狗狗幣剛剛踏入關鍵需求區&#xff0c;此前雖從高點大幅下跌了10%&#xff0c;但XBIT去中心化交易所平臺分析師認為&#xff0c;短期內它有望…

寶塔+fastadmin:給項目添加定時任務

一、定時任務腳本編寫 1. 使用 shebang 聲明執行器 #!/usr/bin/env php 這是 Unix/Linux 系統中腳本文件的標準開頭。表示這個腳本使用系統環境變量中的 php 來執行。2. 定義 ThinkPHP 入口路徑并加載框架 define(APP_PATH, __DIR__ . /../../application/); require __DIR__…