僵尸進程是什么?怎么回收?孤兒進程?

僵尸進程是什么?

  1. 僵尸進程的定義:對于多進程程序,當子進程結束運行但父進程還未讀取其退出狀態時,子進程就處于僵尸態。此時,內核不會立即釋放該子進程的進程表表項,以滿足父進程后續查詢子進程退出信息的需求
  2. 產生原因:子進程運行結束后,父進程若沒有及時獲取其退出狀態,子進程就會一直處于僵尸態;當父進程提前結束或異常終止,而子進程繼續運行時,子進程的 PPID(父進程 ID)會被操作系統設置為 1,即由 init 進程接管。在父進程退出后到子進程退出前這段時間,子進程也處于僵尸態。
  3. 僵尸進程的危害:僵尸進程會一直占據內核資源,而內核資源是有限的。如果大量產生僵尸進程,可能會導致系統資源耗盡,影響系統性能。

處理僵尸進程的函數

????????pid_t wait(int *stat_loc)

????????wait?函數會阻塞調用它的進程(通常是父進程),直到該進程的任意一個子進程結束運行。這意味著調用?wait?后,父進程會暫停執行,等待子進程完成任務。
????????當有子進程結束時,wait?函數返回結束運行的子進程的進程 ID(PID),同時將該子進程的退出狀態信息存儲在?stat_loc?參數指向的內存位置。退出狀態信息包含了子進程是如何結束的,例如是正常退出還是因信號終止等。
????????通過?sys/wait.h?頭文件中定義的宏來解析?stat_loc?中的退出狀態信息:

  • WIFEXITED(stat_val):用于判斷子進程是否正常結束。如果子進程正常結束,該宏返回一個非零值(即真)。
  • WEXITSTATUS(stat_val):當?WIFEXITED?返回非零值時,使用此宏可以獲取子進程的退出碼。子進程通過?exit?函數或從?main?函數返回時設置的退出碼,可以通過這個宏獲取。
  • WIFSIGNALED(stat_val):如果子進程是因為一個未捕獲的信號而終止,此宏返回一個非零值。
  • WTERMSIG(stat_val):當?WIFSIGNALED?返回非零值時,該宏返回導致子進程終止的信號值。例如,如果子進程因接收到?SIGTERM?信號而終止,WTERMSIG?將返回?SIGTERM?的值。
  • WIFSTOPPED(stat_val):若子進程被暫停(例如收到?SIGSTOP?信號),此宏返回一個非零值。
  • WSTOPSIG(stat_val):當?WIFSTOPPED?返回非零值時,該宏返回導致子進程暫停的信號值。

? ? ? ? 示例代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main() {pid_t pid;int status;pid = fork();if (pid == -1) {perror("fork");return 1;} else if (pid == 0) {// 子進程printf("Child process: My PID is %d\n", (int)getpid());exit(10); // 子進程正常退出,退出碼為10} else {// 父進程pid_t terminated_pid = wait(&status);if (terminated_pid == -1) {perror("wait");return 1;}if (WIFEXITED(status)) {printf("Child %d exited normally with exit code %d\n", (int)terminated_pid, WEXITSTATUS(status));} else if (WIFSIGNALED(status)) {printf("Child %d terminated by signal %d\n", (int)terminated_pid, WTERMSIG(status));}}return 0;
}

? ? ? ? 輸出如下:

????????pid_t waitpid(pid_t pid, int *stat_loc, int options)

? ?waitpid?函數提供了比?wait?函數更靈活的等待方式。它可以等待由?pid?參數指定的特定子進程。

  • pid > 0:等待進程 ID 為?pid?的子進程。
  • pid = -1:等待任意一個子進程,此時?waitpid?的行為和?wait?函數相同。
  • pid = 0:等待與調用進程同組的任意子進程。
  • pid < -1:等待進程組 ID 等于?pid?絕對值的任意子進程。

????????與?wait?函數類似,waitpid?函數返回結束運行的子進程的 PID,并將子進程的退出狀態信息存儲在?stat_loc?參數指向的內存位置,同樣可以使用?sys/wait.h?中的宏來解析退出狀態。
????????options?參數可以控制?waitpid?函數的行為。最常用的取值是?WNOHANG,當?options?取值為?WNOHANG?時,waitpid?調用將是非阻塞的。如果?pid?指定的目標子進程還沒有結束或意外終止,則?waitpid?立即返回 0;如果目標子進程確實正常退出了,則?waitpid?返回該子進程的 PID。若?waitpid?調用失敗,返回 -1 并設置?errno

? ?示例代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main() {pid_t pid;int status;pid = fork();if (pid == -1) {perror("fork");return 1;} else if (pid == 0) {// 子進程printf("Child process: My PID is %d\n", (int)getpid());sleep(2); // 模擬子進程執行一些任務exit(10); // 子進程正常退出,退出碼為10} else {// 父進程while (1) {pid_t terminated_pid = waitpid(pid, &status, WNOHANG);if (terminated_pid == -1) {perror("waitpid");return 1;} else if (terminated_pid == 0) {// 子進程還未結束printf("Child is still running...\n");sleep(1);//模擬父進程處理自己的任務} else {if (WIFEXITED(status)) {printf("Child %d exited normally with exit code %d\n", (int)terminated_pid, WEXITSTATUS(status));} else if (WIFSIGNALED(status)) {printf("Child %d terminated by signal %d\n", (int)terminated_pid, WTERMSIG(status));}break;}}}return 0;
}

? ? ? ? 輸出如下:
?

? ? ? ? 講解一下示例代碼:采用的是輪詢的方式檢測子進程是否已經退出,如果未退出waitpid()會返回0,進行下次輪檢測;如果子進程退出了,會打印子進程的退出碼。這樣做的好處是,父進程不必阻塞等待子進程退出,它可以邊等待邊處理自己的任務。

處理僵尸進程更好的方式:利用SIGCHLD信號

? ? ? ? 不斷的輪詢子進程的狀態,絕非明智之舉。要在事件(當然了這里的事件就是子進程退出)已經發生的情況下,執行非阻塞調用才能提高程序的效率。
????????當一個進程結束時,它會給其父進程發送一個SIGCHLD信號。因此,父進程可以捕獲這個信號,并在信號處理函數中調用waitpid函數來處理結束的子進程,從而避免僵尸進程的產生。也就是說:SIGCHLD信號觸發,相當于通知父進程,你的子進程已經退出了。

? ?示例代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>// SIGCHLD信號處理函數
static void handle_child(int sig) {pid_t pid;int stat;// 使用waitpid循環處理已結束的子進程while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) {if (WIFEXITED(stat)) {printf("Child %d exited normally with exit code %d\n", (int)pid, WEXITSTATUS(stat));} else if (WIFSIGNALED(stat)) {printf("Child %d terminated by signal %d\n", (int)pid, WTERMSIG(stat));}}
}int main() {pid_t pid;// 注冊SIGCHLD信號處理函數struct sigaction sa;sa.sa_handler = handle_child;sigemptyset(&sa.sa_mask);sa.sa_flags = SA_RESTART;if (sigaction(SIGCHLD, &sa, NULL) == -1) {perror("sigaction");exit(1);}// 創建子進程pid = fork();if (pid == -1) {perror("fork");exit(1);} else if (pid == 0) {// 子進程printf("Child process: My PID is %d\n", (int)getpid());sleep(2); // 模擬子進程執行一些任務exit(10); // 子進程正常退出,退出碼為10} else {// 父進程printf("Parent process: My PID is %d, Child PID is %d\n", (int)getpid(), (int)pid);// 父進程可以繼續執行其他任務while (1) {printf("Parent is doing other things...\n");sleep(1);}}return 0;
}

? ? ? ? 輸出結果如下:

????????對于初學者來說看這段代碼有些吃力,建議大家在具備了進程信號的知識的前提下再進行閱讀。

什么是孤兒進程?

????????孤兒進程指的是父進程在子進程之前終止,導致子進程失去了父進程的管理和控制。此時,這些子進程會被 init 進程(進程 ID 為 1)收養。init 進程是 Linux 系統啟動后創建的第一個用戶態進程,它負責管理系統中所有孤兒進程的生命周期。
????????如果父進程執行完畢或因異常情況提前終止,而它創建的子進程還在運行,這些子進程就會成為孤兒進程。
? ? ? ? 寫段代碼舉個例子:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>int main() {pid_t pid = fork();if (pid == -1) {perror("fork");return 1;} else if (pid == 0) {// 子進程printf("Child process: My PID is %d, My PPID is %d\n", (int)getpid(), (int)getppid());sleep(5); // 模擬子進程執行任務printf("Child process: After sleeping, My PPID is %d\n", (int)getppid());} else {// 父進程printf("Parent process: My PID is %d, Child PID is %d\n", (int)getpid(), (int)pid);// 父進程提前結束return 0;}return 0;
}

? ? ? ? 輸出結果如下:

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

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

相關文章

[AI繪畫]sd學習記錄(二)文生圖參數進階

目錄 7.高分辨率修復&#xff1a;以小博大8.細化器&#xff08;Refiner&#xff09;&#xff1a;兩模型接力9.隨機數種子&#xff08;Seed&#xff09;&#xff1a;復現圖片吧 本文接續https://blog.csdn.net/qq_23220445/article/details/148460878?spm1001.2014.3001.5501…

C++學習思路

C++知識體系詳細大綱 一、基礎語法 (一)數據類型 基本數據類型 整數類型(int, short, long, long long)浮點類型(float, double, long double)字符類型(char, wchar_t, char16_t, char32_t)布爾類型(bool)復合數據類型 數組結構體(struct)聯合體(union)枚舉類型…

34、協程

在Linux系統中&#xff0c;協程是一種輕量級的線程&#xff0c;它們允許在多個任務之間切換&#xff0c;而不需要操作系統的線程調度。協程可以分為有棧協程和無棧協程&#xff0c;以及對稱協程和非對稱協程。 有棧協程 有棧協程每個協程都有自己的棧空間&#xff0c;允許協程…

DeepSeek 賦能金融科技,重塑開放銀行生態新圖景

目錄 一、金融科技開放銀行生態建設的現狀與挑戰二、DeepSeek 技術解析2.1 DeepSeek 的技術原理與特點2.2 與其他相關技術的對比優勢 三、DeepSeek 在開放銀行生態建設中的具體應用場景3.1 智能客服與財富管理3.2 風控與合規管理3.3 生態協同與數據共享 四、DeepSeek 應用案例分…

【QT控件】輸入類控件詳解

目錄 一、QLineEdit 二、Text Edit 三、Combo Box 四、Spin Box 五、Date Edit & Time Edit 六、Dial 七、Slider QT專欄&#xff1a;QT_uyeonashi的博客-CSDN博客 一、QLineEdit QLineEdit 用來表示單行輸入框. 可以輸入一段文本, 但是不能換行 核心屬性 核心信號…

【Oracle】存儲過程

個人主頁&#xff1a;Guiat 歸屬專欄&#xff1a;Oracle 文章目錄 1. 存儲過程基礎概述1.1 存儲過程的概念與特點1.2 存儲過程的組成結構1.3 存儲過程的優勢 2. 基礎存儲過程2.1 簡單存儲過程2.1.1 創建第一個存儲過程2.1.2 帶變量的存儲過程 2.2 帶參數的存儲過程2.2.1 輸入參…

【MATLAB代碼】制導方法介紹與例程——三點法|三維空間,動態目標導引(訂閱專欄后可直接查看源代碼)

三點法導引是一種導彈制導策略,通過計算導彈、目標和制導站之間的相對位置來確保導彈準確追蹤移動目標。該方法利用三角定位和動態調整,實時更新導彈的飛行路徑,以提高命中率,廣泛應用于軍事導彈和無人機等領域。文中有完整的matlab源代碼,訂閱專欄后即可查看 文章目錄 代…

Servlet 快速入門

文章目錄 概念SpringBoot 測試案例執行原理傳統 Servlet在 SpringBoot &#xff08;嵌入式 Tomcat Spring MVC&#xff09; 中請求從瀏覽器到業務代碼的完整步驟關鍵點流程圖 參考 概念 運行在服務器端的小程序&#xff0c; Servlet 就是一個接口&#xff0c;定義 Java 類被瀏…

Java 8 Stream API 入門到實踐詳解

一、告別 for 循環&#xff01; 傳統痛點&#xff1a; Java 8 之前&#xff0c;集合操作離不開冗長的 for 循環和匿名類。例如&#xff0c;過濾列表中的偶數&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…

黃柏基因組-小檗堿生物合成的趨同進化-文獻精讀142

Convergent evolution of berberine biosynthesis 小檗堿生物合成的趨同進化 摘要 小檗堿是一種有效的抗菌和抗糖尿病生物堿&#xff0c;主要從不同植物譜系中提取&#xff0c;特別是從小檗屬&#xff08;毛茛目&#xff0c;早期分支的真雙子葉植物&#xff09;和黃柏屬&…

機器學習14-遷移學習

遷移學習學習筆記 一、遷移學習概述 遷移學習是機器學習中的一個重要領域&#xff0c;它旨在解決當目標任務的訓練數據有限時&#xff0c;如何利用與目標任務相關但不完全相同的源任務數據來提高學習性能的問題。在現實世界中&#xff0c;獲取大量高質量的標注數據往往成本高…

013旅游網站設計技術詳解:打造一站式旅游服務平臺

旅游網站設計技術詳解&#xff1a;打造一站式旅游服務平臺 在互聯網與旅游業深度融合的時代&#xff0c;旅游網站成為人們規劃行程、預訂服務的重要工具。一個功能完備的旅游網站&#xff0c;通過用戶管理、訂單管理等核心模塊&#xff0c;實現用戶與管理員的高效交互。本文將…

量子計算+AI:特征選擇與神經網絡優化創新應用

在由玻色量子協辦的第二屆APMCM“五岳杯”量子計算挑戰賽中&#xff0c;來自北京理工大學的Q-Masterminds團隊摘取了銀獎。該團隊由北京理工大學張玉利教授指導&#xff0c;依托玻色量子550計算量子比特的相干光量子計算機&#xff0c;將量子計算技術集成到特征選擇和神經網絡剪…

Maven相關問題:jna版本與ES沖突 + aop失效

文章目錄 1、背景2、解決3、一點思考4、環境升級導致AOP失效5、okhttp Bean找不到6、總結 記錄一些Maven依賴相關的思考 1、背景 做一個監控指標收集&#xff0c;用一下jna依賴&#xff1a; <dependency><groupId>net.java.dev.jna</groupId><artifact…

gitlab CI/CD本地部署配置

背景: 代碼管理平臺切換為公司本地服務器的gitlab server。為了保證commit的代碼至少編譯ok&#xff0c;也為了以后能拓展test cases&#xff0c;現在先搭建本地gitlab server的CI/CD基本的編譯job pipeline。 配置步驟&#xff1a; 先安裝gitlab-runner: curl -L "ht…

移除元素-JavaScript【算法學習day.04】

題目鏈接&#xff1a;27. 移除元素 - 力扣&#xff08;LeetCode&#xff09; 第一種思路 標簽&#xff1a;拷貝覆蓋 主要思路是遍歷數組 nums&#xff0c;每次取出的數字變量為 num&#xff0c;同時設置一個下標 ans 在遍歷過程中如果出現數字與需要移除的值不相同時&#xff…

leetcode sql50題

在中文站沒找到對應的集合&#xff0c;想來自己動手拷貝過來&#xff0c;方便大家面試復習用&#xff0c;對應英文站點&#xff1a; https://leetcode.com/studyplan/top-sql-50/ Select #1757. 可回收且低脂的產品 鏈接: https://leetcode.cn/problems/recyclable-and-low-fa…

Dynadot專業版郵箱工具指南(五):將域名郵箱添加至Outlook客戶端

關于Dynadot Dynadot是通過ICANN認證的域名注冊商&#xff0c;自2002年成立以來&#xff0c;服務于全球108個國家和地區的客戶&#xff0c;為數以萬計的客戶提供簡潔&#xff0c;優惠&#xff0c;安全的域名注冊以及管理服務。 Dynadot平臺操作教程索引&#xff08;包括域名郵…

【RTSP從零實踐】1、根據RTSP協議實現一個RTSP服務

&#x1f601;博客主頁&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客內容&#x1f911;&#xff1a;&#x1f36d;嵌入式開發、Linux、C語言、C、數據結構、音視頻&#x1f36d; &#x1f923;本文內容&#x1f923;&a…

Python網頁數據抓取常用的庫及方法介紹

Python網頁數據抓取常用的庫及方法介紹 摘要:以下是Python網絡數據抓取常用的6個庫的詳細介紹,包括它們的概述以及每個庫中最常用的10個函數(如果某些庫常用函數不足10個,則列出所有常用函數)。每個函數都附帶功能描述、用法說明和使用示例。這些庫在網絡爬蟲、數據…