屏蔽收索引擎抓取網(wǎng)站sem競(jìng)價(jià)推廣代運(yùn)營(yíng)收費(fèi)
【僵尸進(jìn)程】
- 目錄:
- 知識(shí)點(diǎn)
- 1. 僵尸進(jìn)程的定義
- 2. 僵尸進(jìn)程產(chǎn)生的原因
- 3. 僵尸進(jìn)程的危害
- 4. 如何避免僵尸進(jìn)程
- 代碼示例
- 產(chǎn)生僵尸進(jìn)程的代碼示例
- 避免僵尸進(jìn)程的代碼示例(父進(jìn)程主動(dòng)回收)
- 避免僵尸進(jìn)程的代碼示例(信號(hào)處理)
- 運(yùn)行結(jié)果
- 產(chǎn)生僵尸進(jìn)程的代碼運(yùn)行結(jié)果
- 避免僵尸進(jìn)程的代碼(父進(jìn)程主動(dòng)回收)運(yùn)行結(jié)果
- 避免僵尸進(jìn)程的代碼(信號(hào)處理)運(yùn)行結(jié)果
- 僵尸進(jìn)程
- 原理
- 實(shí)現(xiàn)方法
- 1. 使用 `wait` 函數(shù)
- 2. 使用 `waitpid` 函數(shù)
- 代碼示例
- 使用 `wait` 函數(shù)
- 使用 `waitpid` 函數(shù)
- 代碼解釋
- 會(huì)自動(dòng)釋放的資源
- 1. 內(nèi)存資源
- 2. 文件描述符
- 3. 信號(hào)處理函數(shù)設(shè)置
- 不會(huì)自動(dòng)釋放的資源
- 1. 進(jìn)程描述符
- 2. 一些共享資源
目錄:
知識(shí)點(diǎn)
1. 僵尸進(jìn)程的定義
在 Linux 系統(tǒng)中,當(dāng)一個(gè)子進(jìn)程結(jié)束運(yùn)行(通過(guò)調(diào)用 exit
或接收到終止信號(hào)),它并不會(huì)立即從系統(tǒng)中消失。子進(jìn)程會(huì)進(jìn)入僵尸狀態(tài)(Zombie State),此時(shí)子進(jìn)程已經(jīng)停止執(zhí)行,但它的進(jìn)程描述符(包含進(jìn)程的退出狀態(tài)等信息)仍然保留在系統(tǒng)中,直到其父進(jìn)程調(diào)用 wait
或 waitpid
等系統(tǒng)調(diào)用獲取其退出狀態(tài),釋放該進(jìn)程描述符,子進(jìn)程才會(huì)真正被銷(xiāo)毀。處于這種狀態(tài)的進(jìn)程就被稱(chēng)為僵尸進(jìn)程。
2. 僵尸進(jìn)程產(chǎn)生的原因
子進(jìn)程先于父進(jìn)程結(jié)束,而父進(jìn)程沒(méi)有及時(shí)調(diào)用 wait
或 waitpid
來(lái)回收子進(jìn)程的資源,導(dǎo)致子進(jìn)程的進(jìn)程描述符一直保留在系統(tǒng)中,從而產(chǎn)生僵尸進(jìn)程。
3. 僵尸進(jìn)程的危害
僵尸進(jìn)程雖然不占用系統(tǒng)的 CPU 資源,但它會(huì)占用系統(tǒng)的進(jìn)程表項(xiàng)。如果系統(tǒng)中存在大量的僵尸進(jìn)程,會(huì)導(dǎo)致進(jìn)程表項(xiàng)被耗盡,從而無(wú)法創(chuàng)建新的進(jìn)程,影響系統(tǒng)的正常運(yùn)行。
4. 如何避免僵尸進(jìn)程
- 父進(jìn)程主動(dòng)回收:父進(jìn)程在子進(jìn)程結(jié)束后,及時(shí)調(diào)用
wait
或waitpid
來(lái)回收子進(jìn)程的資源。 - 信號(hào)處理:父進(jìn)程可以通過(guò)捕獲
SIGCHLD
信號(hào),在信號(hào)處理函數(shù)中調(diào)用wait
或waitpid
來(lái)回收子進(jìn)程的資源。 - 使用
sigaction
函數(shù):結(jié)合sigaction
函數(shù)和SA_NOCLDWAIT
標(biāo)志,避免子進(jìn)程成為僵尸進(jìn)程。
代碼示例
產(chǎn)生僵尸進(jìn)程的代碼示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main() {pid_t pid = fork();if (pid < 0) {perror("fork");return 1;} else if (pid == 0) {// 子進(jìn)程printf("子進(jìn)程 (PID=%d) 即將退出\n", getpid());exit(0);} else {// 父進(jìn)程printf("父進(jìn)程 (PID=%d) 繼續(xù)運(yùn)行,不回收子進(jìn)程資源\n", getpid());sleep(60); // 父進(jìn)程休眠 60 秒,不回收子進(jìn)程資源}return 0;
}
避免僵尸進(jìn)程的代碼示例(父進(jìn)程主動(dòng)回收)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main() {pid_t pid = fork();if (pid < 0) {perror("fork");return 1;} else if (pid == 0) {// 子進(jìn)程printf("子進(jìn)程 (PID=%d) 即將退出\n", getpid());exit(0);} else {// 父進(jìn)程printf("父進(jìn)程 (PID=%d) 等待子進(jìn)程退出并回收資源\n", getpid());int status;pid_t child_pid = wait(&status);if (child_pid > 0) {if (WIFEXITED(status)) {printf("子進(jìn)程 (PID=%d) 正常退出,退出狀態(tài)碼: %d\n", child_pid, WEXITSTATUS(status));}}}return 0;
}
避免僵尸進(jìn)程的代碼示例(信號(hào)處理)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>void sigchld_handler(int signum) {int status;pid_t pid;while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {if (WIFEXITED(status)) {printf("子進(jìn)程 (PID=%d) 正常退出,退出狀態(tài)碼: %d\n", pid, WEXITSTATUS(status));}}
}int main() {signal(SIGCHLD, sigchld_handler);pid_t pid = fork();if (pid < 0) {perror("fork");return 1;} else if (pid == 0) {// 子進(jìn)程printf("子進(jìn)程 (PID=%d) 即將退出\n", getpid());exit(0);} else {// 父進(jìn)程printf("父進(jìn)程 (PID=%d) 繼續(xù)運(yùn)行,等待子進(jìn)程退出信號(hào)\n", getpid());sleep(60);}return 0;
}
運(yùn)行結(jié)果
產(chǎn)生僵尸進(jìn)程的代碼運(yùn)行結(jié)果
子進(jìn)程 (PID=1234) 即將退出
父進(jìn)程 (PID=1233) 繼續(xù)運(yùn)行,不回收子進(jìn)程資源
在父進(jìn)程休眠的 60 秒內(nèi),使用 ps -ef | grep Z
命令可以查看到僵尸進(jìn)程:
USER PID PPID C STIME TTY TIME CMD
youruser 1234 1233 0 10:00 pts/0 00:00:00 [a.out] <defunct>
其中 <defunct>
表示該進(jìn)程是僵尸進(jìn)程。
避免僵尸進(jìn)程的代碼(父進(jìn)程主動(dòng)回收)運(yùn)行結(jié)果
子進(jìn)程 (PID=1234) 即將退出
父進(jìn)程 (PID=1233) 等待子進(jìn)程退出并回收資源
子進(jìn)程 (PID=1234) 正常退出,退出狀態(tài)碼: 0
此時(shí)使用 ps -ef | grep Z
命令不會(huì)查看到僵尸進(jìn)程。
避免僵尸進(jìn)程的代碼(信號(hào)處理)運(yùn)行結(jié)果
子進(jìn)程 (PID=1234) 即將退出
父進(jìn)程 (PID=1233) 繼續(xù)運(yùn)行,等待子進(jìn)程退出信號(hào)
子進(jìn)程 (PID=1234) 正常退出,退出狀態(tài)碼: 0
同樣,使用 ps -ef | grep Z
命令不會(huì)查看到僵尸進(jìn)程。
僵尸進(jìn)程
在 Linux 系統(tǒng)中,父進(jìn)程可以通過(guò)主動(dòng)回收子進(jìn)程資源的方式來(lái)避免僵尸進(jìn)程的產(chǎn)生。下面為你詳細(xì)介紹相關(guān)原理、實(shí)現(xiàn)方法以及代碼示例。
原理
當(dāng)子進(jìn)程結(jié)束時(shí),它會(huì)向父進(jìn)程發(fā)送 SIGCHLD
信號(hào),同時(shí)進(jìn)入僵尸狀態(tài),此時(shí)子進(jìn)程的進(jìn)程描述符仍然保留在系統(tǒng)中,占用系統(tǒng)資源。父進(jìn)程可以通過(guò)調(diào)用 wait
或 waitpid
系統(tǒng)調(diào)用獲取子進(jìn)程的退出狀態(tài),并釋放其進(jìn)程描述符,從而讓子進(jìn)程徹底從系統(tǒng)中消失。
實(shí)現(xiàn)方法
1. 使用 wait
函數(shù)
wait
函數(shù)會(huì)阻塞父進(jìn)程,直到它的任意一個(gè)子進(jìn)程結(jié)束。當(dāng)子進(jìn)程結(jié)束后,wait
函數(shù)會(huì)返回該子進(jìn)程的進(jìn)程 ID,并將子進(jìn)程的退出狀態(tài)存儲(chǔ)在 status
參數(shù)中。
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>pid_t wait(int *status);
- 參數(shù):
status
是一個(gè)指向整數(shù)的指針,用于存儲(chǔ)子進(jìn)程的退出狀態(tài)。如果不需要獲取退出狀態(tài),可以將其設(shè)置為NULL
。 - 返回值:成功時(shí)返回結(jié)束的子進(jìn)程的進(jìn)程 ID;出錯(cuò)時(shí)返回 -1。
2. 使用 waitpid
函數(shù)
waitpid
函數(shù)提供了更靈活的子進(jìn)程回收方式,它可以指定要等待的子進(jìn)程,還可以設(shè)置非阻塞模式。
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>pid_t waitpid(pid_t pid, int *status, int options);
- 參數(shù):
pid
:指定要等待的子進(jìn)程。取值有以下幾種情況:pid > 0
:等待進(jìn)程 ID 為pid
的子進(jìn)程。pid == -1
:等待任意一個(gè)子進(jìn)程,等同于wait
函數(shù)。pid == 0
:等待與調(diào)用進(jìn)程同組的任意子進(jìn)程。pid < -1
:等待進(jìn)程組 ID 等于pid
絕對(duì)值的任意子進(jìn)程。
status
:用于存儲(chǔ)子進(jìn)程的退出狀態(tài),與wait
函數(shù)的status
參數(shù)相同。options
:可以設(shè)置一些選項(xiàng),常用的選項(xiàng)有:WNOHANG
:如果沒(méi)有子進(jìn)程結(jié)束,函數(shù)立即返回 0,而不會(huì)阻塞。WUNTRACED
:如果子進(jìn)程處于暫停狀態(tài),函數(shù)也會(huì)返回。WCONTINUED
:如果子進(jìn)程因收到SIGCONT
信號(hào)而繼續(xù)運(yùn)行,函數(shù)也會(huì)返回。
- 返回值:
- 如果
options
中設(shè)置了WNOHANG
,且沒(méi)有子進(jìn)程結(jié)束,返回 0。 - 成功時(shí)返回結(jié)束的子進(jìn)程的進(jìn)程 ID。
- 出錯(cuò)時(shí)返回 -1。
- 如果
代碼示例
使用 wait
函數(shù)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main() {pid_t pid = fork();if (pid < 0) {perror("fork");return 1;} else if (pid == 0) {// 子進(jìn)程printf("子進(jìn)程 (PID=%d) 即將退出\n", getpid());exit(0);} else {// 父進(jìn)程printf("父進(jìn)程 (PID=%d) 等待子進(jìn)程退出并回收資源\n", getpid());int status;pid_t child_pid = wait(&status);if (child_pid > 0) {if (WIFEXITED(status)) {printf("子進(jìn)程 (PID=%d) 正常退出,退出狀態(tài)碼: %d\n", child_pid, WEXITSTATUS(status));}}}return 0;
}
使用 waitpid
函數(shù)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main() {pid_t pid = fork();if (pid < 0) {perror("fork");return 1;} else if (pid == 0) {// 子進(jìn)程printf("子進(jìn)程 (PID=%d) 即將退出\n", getpid());exit(0);} else {// 父進(jìn)程printf("父進(jìn)程 (PID=%d) 等待子進(jìn)程退出并回收資源\n", getpid());int status;pid_t child_pid = waitpid(pid, &status, 0);if (child_pid > 0) {if (WIFEXITED(status)) {printf("子進(jìn)程 (PID=%d) 正常退出,退出狀態(tài)碼: %d\n", child_pid, WEXITSTATUS(status));}}}return 0;
}
代碼解釋
在上述代碼中,首先使用
fork
函數(shù)創(chuàng)建一個(gè)子進(jìn)程。
- 子進(jìn)程打印一條信息后調(diào)用
exit
函數(shù)退出。- 父進(jìn)程打印一條信息后,調(diào)用
wait
或waitpid
函數(shù)等待子進(jìn)程退出,并獲取其退出狀態(tài)。- 使用
WIFEXITED
宏判斷子進(jìn)程是否正常退出,如果是,則使用WEXITSTATUS
宏獲取子進(jìn)程的退出狀態(tài)碼并打印。
通過(guò)這種方式,父進(jìn)程可以及時(shí)回收子進(jìn)程的資源,避免僵尸進(jìn)程的產(chǎn)生。
子進(jìn)程在退出時(shí)會(huì)自動(dòng)釋放一部分資源,但并非所有資源都會(huì)自動(dòng)釋放,下面為你詳細(xì)介紹:
會(huì)自動(dòng)釋放的資源
1. 內(nèi)存資源
- 棧和堆內(nèi)存:子進(jìn)程在運(yùn)行過(guò)程中在棧上分配的局部變量和在堆上通過(guò)
malloc
、calloc
、realloc
等函數(shù)分配的內(nèi)存,在子進(jìn)程退出時(shí)會(huì)被自動(dòng)釋放。例如,在子進(jìn)程中使用malloc
分配了一塊內(nèi)存用于存儲(chǔ)數(shù)據(jù),當(dāng)子進(jìn)程退出時(shí),操作系統(tǒng)會(huì)回收這塊內(nèi)存,防止內(nèi)存泄漏。 - 數(shù)據(jù)段和代碼段:子進(jìn)程的代碼段(存儲(chǔ)程序的可執(zhí)行指令)和數(shù)據(jù)段(存儲(chǔ)全局變量和靜態(tài)變量)所占用的內(nèi)存空間也會(huì)在子進(jìn)程退出時(shí)被釋放。
2. 文件描述符
子進(jìn)程在運(yùn)行過(guò)程中打開(kāi)的文件描述符(如文件、套接字等),在子進(jìn)程退出時(shí)會(huì)被操作系統(tǒng)自動(dòng)關(guān)閉 ,例如,子進(jìn)程打開(kāi)了一個(gè)文件進(jìn)行讀寫(xiě)操作,當(dāng)子進(jìn)程退出時(shí),該文件描述符會(huì)被關(guān)閉,相應(yīng)的文件資源會(huì)被釋放。
3. 信號(hào)處理函數(shù)設(shè)置
子進(jìn)程所設(shè)置的信號(hào)處理函數(shù)等相關(guān)設(shè)置在其退出時(shí)會(huì)失效,操作系統(tǒng)會(huì)清理這些與子進(jìn)程相關(guān)的信號(hào)處理信息。
不會(huì)自動(dòng)釋放的資源
1. 進(jìn)程描述符
子進(jìn)程退出后,其進(jìn)程描述符(包含進(jìn)程的狀態(tài)信息、退出狀態(tài)、資源使用統(tǒng)計(jì)等)并不會(huì)自動(dòng)釋放。子進(jìn)程會(huì)進(jìn)入僵尸狀態(tài)(Zombie State),此時(shí)它的進(jìn)程描述符仍然保留在系統(tǒng)中,直到其父進(jìn)程調(diào)用wait
或waitpid
等系統(tǒng)調(diào)用獲取其退出狀態(tài),釋放該進(jìn)程描述符,子進(jìn)程才會(huì)真正從系統(tǒng)中消失。如果父進(jìn)程沒(méi)有及時(shí)回收子進(jìn)程的進(jìn)程描述符,就會(huì)產(chǎn)生僵尸進(jìn)程。
2. 一些共享資源
- 共享內(nèi)存:如果子進(jìn)程使用了共享內(nèi)存(通過(guò)
shmget
、shmat
等函數(shù)創(chuàng)建或關(guān)聯(lián)),在子進(jìn)程退出時(shí),共享內(nèi)存本身不會(huì)自動(dòng)釋放。共享內(nèi)存是多個(gè)進(jìn)程可以共享的內(nèi)存區(qū)域,子進(jìn)程的退出不會(huì)影響其他進(jìn)程對(duì)共享內(nèi)存的使用,需要顯式地調(diào)用shmdt
(分離共享內(nèi)存)和shmctl
(刪除共享內(nèi)存)等函數(shù)來(lái)釋放共享內(nèi)存資源。 - 信號(hào)量:子進(jìn)程使用的信號(hào)量(通過(guò)
semget
、semop
等函數(shù)操作)在其退出時(shí)也不會(huì)自動(dòng)釋放。信號(hào)量是用于進(jìn)程間同步的機(jī)制,需要顯式地調(diào)用semctl
等函數(shù)來(lái)刪除信號(hào)量集。
綜上所述,雖然子進(jìn)程在退出時(shí)會(huì)自動(dòng)釋放一部分資源,但對(duì)于進(jìn)程描述符和一些共享資源,需要父進(jìn)程或其他進(jìn)程進(jìn)行顯式的處理,以確保系統(tǒng)資源的正確釋放和回收。