會(huì)計(jì)可以做網(wǎng)站么真實(shí)的網(wǎng)站制作
個(gè)人主頁:chian-ocean
文章專欄-Linux
前言:
當(dāng)一個(gè)進(jìn)程發(fā)起某種操作(如I/O請(qǐng)求、信號(hào)、鎖的獲取等),但該操作需要的資源暫時(shí)不可用時(shí),進(jìn)程會(huì)被操作系統(tǒng)掛起,進(jìn)入“等待隊(duì)列”或“阻塞狀態(tài)”。在此期間,進(jìn)程不占用CPU,但仍保留其內(nèi)存、文件描述符等資源
進(jìn)程等待的必要性
僵尸進(jìn)程的存在
僵尸進(jìn)程的成因
- 當(dāng)子進(jìn)程終止后,它的退出狀態(tài)需要由父進(jìn)程通過調(diào)用
wait()
或waitpid()
系統(tǒng)調(diào)用回收。 - 如果父進(jìn)程未回收子進(jìn)程的退出狀態(tài),子進(jìn)程會(huì)以“僵尸進(jìn)程”的形式保留在進(jìn)程表中。
特征:
- 在 Linux 系統(tǒng)中,可以用
ps
命令查看,僵尸進(jìn)程的狀態(tài)為Z
(Zombie)。 - 僵尸進(jìn)程是操作系統(tǒng)保留的一個(gè)條目,主要用于父進(jìn)程檢查子進(jìn)程的退出狀態(tài)。
如下:
從圖片中可以看到一個(gè)典型的 僵尸進(jìn)程 的現(xiàn)象:
- 進(jìn)程
27864
被強(qiáng)制終止(kill -9 27864
),但它的父進(jìn)程(27863
)沒有調(diào)用wait()
或waitpid()
來回收其子進(jìn)程的退出狀態(tài)。 - 因此,
27864
被標(biāo)記為<defunct>
狀態(tài),即僵尸進(jìn)程。 ps
輸出的STAT
列中顯示Z+
,這是僵尸進(jìn)程的狀態(tài)標(biāo)識(shí)。
進(jìn)程等待
進(jìn)程等待是操作系統(tǒng)中一種重要的狀態(tài),指的是某個(gè)進(jìn)程由于資源不足或條件未滿足,暫時(shí)無法繼續(xù)執(zhí)行而被掛起的現(xiàn)象。
- 使用
wait()
或waitpid()
回收子進(jìn)程
wait ( )
參數(shù):
-
int *status:
- 用于保存子進(jìn)程的狀態(tài)信息(如退出碼或終止信號(hào))。
- 如果不需要獲取子進(jìn)程狀態(tài),可以將其傳入
NULL
。
返回值:
- 成功:
- 返回已終止的子進(jìn)程的 PID。
- 失敗:
- 返回
-1
,并設(shè)置errno
。 - 常見錯(cuò)誤包括:
ECHILD
:當(dāng)前進(jìn)程沒有子進(jìn)程。EINTR
:調(diào)用被信號(hào)中斷。
- 返回
wait()
的作用
- 阻塞父進(jìn)程:
wait()
會(huì)阻塞父進(jìn)程,直到任意一個(gè)子進(jìn)程狀態(tài)發(fā)生變化(通常是終止)。
- 回收子進(jìn)程資源:
- 子進(jìn)程終止后,其資源仍然保留在系統(tǒng)中,直到父進(jìn)程調(diào)用
wait()
或waitpid()
回收它。 - 如果父進(jìn)程不調(diào)用
wait()
或waitpid()
,子進(jìn)程會(huì)變成 僵尸進(jìn)程。
- 子進(jìn)程終止后,其資源仍然保留在系統(tǒng)中,直到父進(jìn)程調(diào)用
示例:
#include<iostream>
#include<unistd.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;void childtast()
{for(int i = 0; i < 10; i++) // 循環(huán)打印從 0 到 9 的數(shù)字{cout << i << endl; // 輸出當(dāng)前的循環(huán)變量 i}sleep(3); // 睡眠 3 秒,模擬子進(jìn)程的運(yùn)行延遲
}int main()
{pid_t id = fork(); // 創(chuàng)建子進(jìn)程cout << "id" << ":" << id << endl;if(id == 0) // 判斷是否是子進(jìn)程{sleep(3); // 子進(jìn)程先睡眠 3 秒childtast(); // 子進(jìn)程調(diào)用 childtast(),打印數(shù)字并睡眠}// 父進(jìn)程等待任意一個(gè)子進(jìn)程終止pid_t ret = wait(NULL); // 父進(jìn)程調(diào)用 wait(),阻塞等待子進(jìn)程終止if(ret == id) // 判斷 wait() 返回的進(jìn)程 ID 是否是創(chuàng)建的子進(jìn)程 ID{cout << "ret" << ":" << ret << endl; // 輸出子進(jìn)程的 IDcout << "wait success" << endl; // 輸出等待成功的消息}sleep(3); // 父進(jìn)程再睡眠 3 秒,模擬延遲return 0;
}
fork()
創(chuàng)建子進(jìn)程:
- 父進(jìn)程和子進(jìn)程同時(shí)運(yùn)行。
- 父進(jìn)程的
id
是子進(jìn)程的 PID,子進(jìn)程的id
是 0。
子進(jìn)程的任務(wù):
- 子進(jìn)程先睡眠 3 秒,然后執(zhí)行
childtast()
,打印0
到9
。
父進(jìn)程的等待:
- 父進(jìn)程調(diào)用
wait(NULL)
,阻塞自身,直到子進(jìn)程終止。 - 當(dāng)子進(jìn)程完成任務(wù)并退出后,
wait()
返回子進(jìn)程的 PID。
父進(jìn)程的后續(xù)操作:
- 父進(jìn)程輸出子進(jìn)程的
PID
和等待成功的消息。 - 父進(jìn)程再睡眠 3 秒后退出。
waitpid ( )
waitpid()
是 wait()
的增強(qiáng)版本,提供了更靈活的功能,允許父進(jìn)程:
- 等待特定的子進(jìn)程。
- 非阻塞等待子進(jìn)程。
- 獲取子進(jìn)程的狀態(tài)(如退出狀態(tài)或被信號(hào)終止)。
pid_t waitpid(pid_t pid, int *status, int options);
參數(shù)說明
-
pid
:-
pid > 0
:等待特定的子進(jìn)程(指定的 PID)。 -
pid == 0
:等待與當(dāng)前進(jìn)程同一個(gè)進(jìn)程組的任意子進(jìn)程。 -
pid < -1
:等待進(jìn)程組 ID 為|pid|
的任意子進(jìn)程。wait(NULL) //等價(jià)于 waitpid(-1,NULL,0);
-
pid == -1
:等效于wait()
,等待任意子進(jìn)程。
-
status
字段的結(jié)構(gòu)
status
:
- 指向一個(gè)整數(shù)的指針,用于存儲(chǔ)子進(jìn)程的狀態(tài)信息(退出狀態(tài)、信號(hào)等)。
- 若不關(guān)心狀態(tài)信息,可將其設(shè)為
NULL
。
在 Linux 系統(tǒng)中,status
是一個(gè)整數(shù),表示子進(jìn)程狀態(tài)的多種可能性,底層通過位字段表示:
位字段 | 含義 |
---|---|
位 0-7 | 子進(jìn)程退出的信號(hào)或退出碼(低 8 位)。 |
位 8-15 | 退出狀態(tài)(高 8 位,存儲(chǔ)正常退出碼)。 |
位 16-23 | 暫停信號(hào)編號(hào)。 |
代碼解析字段
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;
int main()
{ pid_t id = fork(); cout << "id" << ":" << id <<endl; if(id == 0) { sleep(3); exit(1); } int status; pid_t ret = waitpid(-1,&status,0); if(ret == id) {cout << "ret" << ":" << ret <<endl; cout<< "wait success" <<endl; } cout <<"status :" << status << endl;cout << "退出碼" << ((status >> 8)& 0xff ) <<" "<< "信號(hào)碼" << (status & 0x7f)<< endl;return 0;
}
完整運(yùn)行流程
fork()
創(chuàng)建子進(jìn)程:
- 父進(jìn)程創(chuàng)建子進(jìn)程,并返回子進(jìn)程的 PID。
子進(jìn)程邏輯:
- 子進(jìn)程休眠 3 秒后正常退出,退出碼為
1
。
父進(jìn)程邏輯:
- 父進(jìn)程調(diào)用
waitpid()
阻塞等待子進(jìn)程終止。 - 獲取子進(jìn)程的狀態(tài)信息,并解析退出碼和信號(hào)碼。
父進(jìn)程輸出狀態(tài)信息:
- 輸出子進(jìn)程的 PID、狀態(tài)值、退出碼和信號(hào)碼。
解析邏輯:
-
退出碼:
(status >> 8) & 0xff
- 獲取高 8 位的退出碼。
-
信號(hào)碼:
status & 0x7f
- 獲取低 7 位的信號(hào)碼.
示例1:進(jìn)程正常退出的退出碼。
示例2:提取被9號(hào)信號(hào)殺死的進(jìn)程信號(hào)碼
id:1667 // 父進(jìn)程輸出,子進(jìn)程的 PID 是 1667 id:0 // 子進(jìn)程輸出,表明當(dāng)前是子進(jìn)程 ret:1667 // 父進(jìn)程成功等待到子進(jìn)程結(jié)束,返回子進(jìn)程 PID wait success // 父進(jìn)程確認(rèn)子進(jìn)程終止 status :9 // 父進(jìn)程獲取子進(jìn)程狀態(tài)值為 9 退出碼0 信號(hào)碼9 // 父進(jìn)程解析狀態(tài)值:// - 退出碼 0:子進(jìn)程未通過 exit() 返回退出碼// - 信號(hào)碼 9:子進(jìn)程被 SIGKILL 信號(hào)終止
庫中提供的宏替換
解析退出碼和信號(hào)編號(hào)
WIFEXITED(status)
:- 如果為真,表示子進(jìn)程正常退出,其退出碼存儲(chǔ)在高 8 位。
- 使用
(status >> 8) & 0xff
提取退出碼。
WEXITSTATUS(status)
:- 獲取退出碼的宏,等價(jià)于
(status >> 8) & 0xff
。 - 必須確保
WIFEXITED(status)
為真后使用。
- 獲取退出碼的宏,等價(jià)于
解析退出碼和信號(hào)編號(hào)
WIFEXITED(status)
- 如果為真,表示子進(jìn)程正常退出,其退出碼存儲(chǔ)在高 8 位。
- 使用
(status >> 8) & 0xff
提取退出碼。
WEXITSTATUS(status)
:==status & 0x7f
- 獲取退出碼的宏,
- 必須確保
WIFEXITED(status)
為真后使用。
options
參數(shù)介紹
阻塞與非阻塞
特性 | 阻塞 | 非阻塞 |
---|---|---|
進(jìn)程狀態(tài) | 等待資源時(shí)掛起,無法執(zhí)行其他任務(wù)。 | 立即返回,不會(huì)掛起,進(jìn)程可執(zhí)行其他任務(wù)。 |
適用場景 | 簡單任務(wù)、對(duì)實(shí)時(shí)性要求不高的任務(wù)。 | 多任務(wù)并發(fā)、實(shí)時(shí)性要求高的任務(wù)。 |
復(fù)雜性 | 實(shí)現(xiàn)簡單,邏輯清晰。 | 邏輯復(fù)雜,需要輪詢或回調(diào)處理資源狀態(tài)。 |
CPU 使用 | 不浪費(fèi) CPU 資源,進(jìn)程處于掛起狀態(tài)。 | 需要輪詢資源狀態(tài),可能增加 CPU 占用。 |
資源管理 | 等待資源的管理交由操作系統(tǒng)處理。 | 需要程序主動(dòng)檢查資源狀態(tài),增加開發(fā)復(fù)雜度。 |
options
:
- 用于指定額外的選項(xiàng):
0
:阻塞等待。WNOHANG
:非阻塞等待。WUNTRACED
:返回暫停的子進(jìn)程狀態(tài)(子進(jìn)程因SIGSTOP
信號(hào)暫停)。WCONTINUED
:返回恢復(fù)運(yùn)行的子進(jìn)程狀態(tài)(子進(jìn)程因SIGCONT
信號(hào)繼續(xù)運(yùn)行)。
WNOHANG
- 非阻塞模式:
- 如果沒有子進(jìn)程終止,
waitpid()
會(huì)立即返回,而不是阻塞父進(jìn)程。
- 如果沒有子進(jìn)程終止,
- 返回值:
- 如果有子進(jìn)程狀態(tài)變化,則返回子進(jìn)程的 PID。
- 如果沒有子進(jìn)程狀態(tài)變化,則返回
0
。
非阻塞輪詢
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <chrono>
#include <thread>
using namespace std;
int main() {pid_t pid = fork(); // 創(chuàng)建子進(jìn)程if (pid == 0) {// 子進(jìn)程邏輯cout << "Child process running..." << endl;sleep(5); // 模擬子進(jìn)程任務(wù),延遲 5 秒cout << "Child process exiting..." << endl;exit(42); // 子進(jìn)程以退出碼 42 正常退出} else if (pid > 0) {// 父進(jìn)程邏輯int status;while (true) {pid_t ret = waitpid(-1, &status, WNOHANG); // 非阻塞檢查子進(jìn)程狀態(tài)if (ret == 0) {// 子進(jìn)程尚未終止,父進(jìn)程繼續(xù)其他工作cout << "Child process still running. Parent doing other work..." << endl;this_thread::sleep_for(chrono::seconds(1)); // 模擬父進(jìn)程任務(wù)} else if (ret > 0) {// 子進(jìn)程已終止,解析狀態(tài)if (WIFEXITED(status)) {cout << "Child process " << ret << " exited with code " << WEXITSTATUS(status) << endl;} else if (WIFSIGNALED(status)) {cout << "Child process " << ret << " was terminated by signal " << WTERMSIG(status) << endl;}break; // 結(jié)束輪詢} else {// waitpid 出錯(cuò)perror("waitpid failed");break;}}} else {// fork 失敗perror("fork failed");return 1;}return 0;
}
執(zhí)行結(jié)果:
多進(jìn)程下的進(jìn)程等待
阻塞等待多個(gè)子進(jìn)程
示例代碼:等待所有子進(jìn)程完成
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;int main() {// 創(chuàng)建多個(gè)子進(jìn)程for (int i = 0; i < 3; ++i) {pid_t pid = fork();if (pid == 0) {// 子進(jìn)程cout << "Child " << i << " (PID: " << getpid() << ") running..." << endl;sleep(2 + i); // 每個(gè)子進(jìn)程休眠不同時(shí)間cout << "Child " << i << " (PID: " << getpid() << ") exiting..." << endl;exit(i); // 子進(jìn)程以其序號(hào)為退出碼}}// 父進(jìn)程:等待所有子進(jìn)程完成int status;while (true) {pid_t ret = wait(&status); // 阻塞等待任意一個(gè)子進(jìn)程結(jié)束if (ret == -1) {// 沒有子進(jìn)程可等待時(shí)退出循環(huán)cout << "All child processes have finished." << endl;break;// 解析子進(jìn)程狀態(tài)if (WIFEXITED(status)) {cout << "Child process " << ret << " exited with code: " << WEXITSTATUS(status) << endl;} else if (WIFSIGNALED(status)) {cout << "Child process " << ret << " was terminated by signal: " << WTERMSIG(status) << endl;}}return 0;
}
代碼執(zhí)行:
非阻塞輪詢等待多個(gè)子進(jìn)程
示例代碼:非阻塞等待多個(gè)子進(jìn)程
通過 waitpid()
配合 WNOHANG
實(shí)現(xiàn)父進(jìn)程的非阻塞輪詢,定期檢查是否有子進(jìn)程完成。
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <chrono>
#include <thread>
using namespace std;int main() {// 創(chuàng)建多個(gè)子進(jìn)程for (int i = 0; i < 3; ++i) {pid_t pid = fork();if (pid == 0) {// 子進(jìn)程cout << "Child " << i << " (PID: " << getpid() << ") running..." << endl;sleep(2 + i); // 每個(gè)子進(jìn)程休眠不同時(shí)間cout << "Child " << i << " (PID: " << getpid() << ") exiting..." << endl;exit(i); // 子進(jìn)程以其序號(hào)為退出碼}}// 父進(jìn)程:非阻塞輪詢等待所有子進(jìn)程完成int status;int completed = 0; // 已完成的子進(jìn)程計(jì)數(shù)while (completed < 3) {pid_t ret = waitpid(-1, &status, WNOHANG); // 非阻塞檢查子進(jìn)程狀態(tài)if (ret > 0) {// 有子進(jìn)程狀態(tài)變化completed++;if (WIFEXITED(status)) {cout << "Child process " << ret << " exited with code: " << WEXITSTATUS(status) << endl;} else if (WIFSIGNALED(status)) {cout << "Child process " << ret << " was terminated by signal: " << WTERMSIG(status) << endl;}} else if (ret == 0) {// 沒有子進(jìn)程狀態(tài)變化,父進(jìn)程繼續(xù)其他工作cout << "No child process exited yet. Parent doing other work..." << endl;this_thread::sleep_for(chrono::seconds(1)); // 模擬其他任務(wù)} else {// 錯(cuò)誤處理perror("waitpid failed");break;}}cout << "All child processes have finished." << endl;return 0;
}
代碼執(zhí)行: