嵌入式Linux:如何監(jiān)視子進(jìn)程
在嵌入式Linux系統(tǒng)中,父進(jìn)程通常需要?jiǎng)?chuàng)建子進(jìn)程來(lái)執(zhí)行特定任務(wù),例如處理網(wǎng)絡(luò)請(qǐng)求、執(zhí)行計(jì)算任務(wù)等。
1
wait()函數(shù)
wait()系統(tǒng)調(diào)用用于讓父進(jìn)程等待任意一個(gè)子進(jìn)程的終止,并獲取該子進(jìn)程的終止?fàn)顟B(tài)信息。
它執(zhí)行以下功能:
等待子進(jìn)程終止:父進(jìn)程在調(diào)用wait()后會(huì)阻塞,直到其任意一個(gè)子進(jìn)程終止為止。
回收子進(jìn)程資源:當(dāng)子進(jìn)程終止時(shí),操作系統(tǒng)需要回收它占用的資源,這一過(guò)程稱(chēng)為“收尸”。如果不進(jìn)行回收,子進(jìn)程會(huì)變?yōu)榻┦M(jìn)程,占用系統(tǒng)資源。
僵尸進(jìn)程是已經(jīng)終止,但父進(jìn)程尚未讀取其終止?fàn)顟B(tài)的子進(jìn)程。通過(guò)調(diào)用wait()可以避免系統(tǒng)中積累僵尸進(jìn)程,影響性能和穩(wěn)定性。
函數(shù)原型如下:
#include <sys/types.h>#include <sys/wait.h>pid_t wait(int *status);
參數(shù)與返回值:
status:這是一個(gè)指向int的指針,用于存儲(chǔ)子進(jìn)程的退出狀態(tài)。父進(jìn)程可以通過(guò)這個(gè)狀態(tài)了解子進(jìn)程是正常退出還是被信號(hào)中止的。如果傳入NULL,則表示不關(guān)心子進(jìn)程的退出狀態(tài),僅僅是等待它終止。
返回值:返回已終止的子進(jìn)程的進(jìn)程ID;如果調(diào)用時(shí)沒(méi)有子進(jìn)程存在,wait()返回-1,并將errno設(shè)為ECHILD表示沒(méi)有子進(jìn)程可等待。
函數(shù)行為:
阻塞等待:wait()會(huì)阻塞調(diào)用進(jìn)程,直到任意一個(gè)子進(jìn)程終止。如果所有子進(jìn)程都還在運(yùn)行,wait()將持續(xù)阻塞。
資源回收:當(dāng)子進(jìn)程終止時(shí),wait()除了獲取子進(jìn)程的終止?fàn)顟B(tài),還會(huì)回收子進(jìn)程的資源,避免產(chǎn)生僵尸進(jìn)程。
處理已終止的子進(jìn)程:如果wait()調(diào)用時(shí)有子進(jìn)程已終止,函數(shù)將立即返回,而不會(huì)阻塞。
狀態(tài)檢查,使用宏可以檢查和處理status參數(shù)中存儲(chǔ)的子進(jìn)程終止?fàn)顟B(tài):
WIFEXITED(status):如果子進(jìn)程是通過(guò)exit()或_exit()正常終止的,則返回true。
WEXITSTATUS(status):當(dāng)WIFEXITED(status)為true時(shí),可以通過(guò)該宏獲取子進(jìn)程的退出狀態(tài),通常是子進(jìn)程在調(diào)用exit()或_exit()時(shí)的退出碼。
WIFSIGNALED(status):如果子進(jìn)程因接收到某個(gè)信號(hào)而異常終止,則返回true。
WTERMSIG(status):當(dāng)WIFSIGNALED(status)為true時(shí),可以通過(guò)該宏獲取導(dǎo)致子進(jìn)程終止的信號(hào)編號(hào)。
WIFSTOPPED(status):如果子進(jìn)程處于暫停狀態(tài),則返回true。
WSTOPSIG(status):當(dāng)WIFSTOPPED(status)為true時(shí),可以獲取導(dǎo)致子進(jìn)程暫停的信號(hào)編號(hào)。
WCOREDUMP(status):如果子進(jìn)程終止時(shí)生成了核心轉(zhuǎn)儲(chǔ)文件,則返回true。
以下示例展示了如何使用wait()函數(shù)來(lái)監(jiān)視子進(jìn)程的終止?fàn)顟B(tài)。
#include <sys/types.h>#include <sys/wait.h>#include <unistd.h>#include <stdio.h>#include <stdlib.h> int main() { pid_t pid = fork(); // 創(chuàng)建子進(jìn)程 if (pid == -1) { // fork()失敗 perror("fork failed"); exit(EXIT_FAILURE); } else if (pid == 0) { // 子進(jìn)程執(zhí)行代碼 printf("Child process running (PID: %d)...n", getpid()); sleep(2); // 模擬子進(jìn)程的執(zhí)行 exit(42); // 正常退出,并返回狀態(tài)碼42 } else { // 父進(jìn)程執(zhí)行代碼 int status; pid_t child_pid = wait(&status); // 等待任一子進(jìn)程終止 if (child_pid > 0) { // 子進(jìn)程終止后的處理 if (WIFEXITED(status)) { printf("Child process %d terminated with status: %dn", child_pid, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf("Child process %d was terminated by signal: %dn", child_pid, WTERMSIG(status)); } } else { perror("wait failed"); } } return 0;}
在這段代碼中,父進(jìn)程創(chuàng)建了一個(gè)子進(jìn)程并等待其終止,同時(shí)通過(guò)status宏獲取子進(jìn)程的退出狀態(tài)。
wait()函數(shù)的局限性:
無(wú)法指定特定子進(jìn)程:wait()無(wú)法讓父進(jìn)程選擇等待某個(gè)特定的子進(jìn)程,它只能按順序等待下一個(gè)終止的子進(jìn)程。如果父進(jìn)程同時(shí)擁有多個(gè)子進(jìn)程,wait()將隨機(jī)處理任意一個(gè)子進(jìn)程的終止。
阻塞等待:wait()始終是阻塞的,直到有子進(jìn)程終止為止。如果父進(jìn)程需要繼續(xù)處理其他任務(wù),則wait()的阻塞可能導(dǎo)致父進(jìn)程效率低下。
2
waitpid()函數(shù)
waitpid()函數(shù)提供了更多的控制選項(xiàng),使得父進(jìn)程可以選擇性地等待某個(gè)特定子進(jìn)程,或進(jìn)行非阻塞的等待。
函數(shù)原型如下:
#include <sys/types.h>#include <sys/wait.h>pid_t waitpid(pid_t pid, int *status, int options);
參數(shù):
pid:指定需要等待的子進(jìn)程:
> 0:等待指定PID的子進(jìn)程。
= 0:等待與調(diào)用進(jìn)程同一進(jìn)程組的任意子進(jìn)程。
< -1:等待進(jìn)程組ID等于pid絕對(duì)值的所有子進(jìn)程。
= -1:等待任意子進(jìn)程,與wait()等價(jià)。
status:與wait()的status參數(shù)相同。
options:可以設(shè)置為0或包含以下標(biāo)志:
WNOHANG:非阻塞模式。如果沒(méi)有子進(jìn)程終止,立即返回0。
WUNTRACED:返回因信號(hào)停止的子進(jìn)程的狀態(tài)。
WCONTINUED:返回收到SIGCONT信號(hào)后恢復(fù)運(yùn)行的子進(jìn)程的狀態(tài)。
返回值:
成功時(shí),返回已終止或狀態(tài)已改變的子進(jìn)程的PID。
如果沒(méi)有符合條件的子進(jìn)程,且設(shè)置了WNOHANG選項(xiàng),返回0。
失敗時(shí)返回-1,并設(shè)置errno。
waitpid()與wait()的區(qū)別:
等待特定子進(jìn)程:waitpid()允許父進(jìn)程通過(guò)pid參數(shù)指定特定的子進(jìn)程,而wait()只能等待任意子進(jìn)程。
非阻塞模式:waitpid()支持非阻塞模式(通過(guò)WNOHANG),使父進(jìn)程可以立即返回,而不必等待子進(jìn)程終止。
支持更多狀態(tài)監(jiān)控:waitpid()可以監(jiān)視子進(jìn)程暫停(WUNTRACED)或恢復(fù)運(yùn)行(WCONTINUED)的狀態(tài),而wait()無(wú)法做到這一點(diǎn)。
示例代碼:
#include <sys/types.h>#include <sys/wait.h>#include <unistd.h>#include <stdio.h>#include <stdlib.h> int main() { pid_t pid = fork(); // 創(chuàng)建子進(jìn)程 if (pid == -1) { // fork()失敗 perror("fork failed"); exit(EXIT_FAILURE); } else if (pid == 0) { // 子進(jìn)程執(zhí)行代碼 printf("Child process running (PID: %d)...n", getpid()); sleep(2); // 模擬子進(jìn)程工作 exit(42); // 正常退出,返回狀態(tài)碼42 } else { // 父進(jìn)程執(zhí)行代碼 int status; pid_t child_pid; // 非阻塞等待子進(jìn)程 do { child_pid = waitpid(pid, &status, WNOHANG); // 非阻塞模式 if (child_pid == 0) { printf("No child process terminated yet. Doing other work...n"); sleep(1); // 模擬其他工作 } else if (child_pid > 0) { if (WIFEXITED(status)) { printf("Child process %d terminated with status: %dn", child_pid, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf("Child process %d was terminated by signal: %dn", child_pid, WTERMSIG(status)); } } else { perror("waitpid failed"); exit(EXIT_FAILURE); } } while (child_pid == 0); printf("Parent process continues...n"); } return 0;}
在這個(gè)示例中,父進(jìn)程可以繼續(xù)處理其他任務(wù),而不必一直阻塞等待子進(jìn)程的終止。waitpid()的非阻塞模式使得程序更為靈活和高效。
3
SIGCHLD信號(hào)
SIGCHLD是父進(jìn)程在子進(jìn)程狀態(tài)發(fā)生變化(如終止或暫停)時(shí)收到的信號(hào)。通過(guò)捕獲SIGCHLD信號(hào),父進(jìn)程可以實(shí)時(shí)地檢測(cè)到子進(jìn)程的狀態(tài)變化,并采取相應(yīng)的行動(dòng)(例如回收資源)。
在POSIX標(biāo)準(zhǔn)下,sigaction()系統(tǒng)調(diào)用被廣泛用于設(shè)置信號(hào)處理程序。相比于傳統(tǒng)的signal()函數(shù),sigaction()提供了更多的選項(xiàng)和更好的控制。
示例代碼:
#include <sys/types.h>#include <sys/wait.h>#include <unistd.h>#include <signal.h>#include <stdio.h>#include <stdlib.h> void sigchld_handler(int signum) { int status; pid_t pid; // 循環(huán)調(diào)用waitpid,以確保處理多個(gè)已終止的子進(jìn)程 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { if (WIFEXITED(status)) { printf("Child process %d terminated with status: %dn", pid, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf("Child process %d was terminated by signal: %dn", pid, WTERMSIG(status)); } }} int main() { struct sigaction sa; sa.sa_handler = sigchld_handler; // 指定信號(hào)處理函數(shù) sigemptyset(&sa.sa_mask); // 清空阻塞信號(hào)集 sa.sa_flags = SA_RESTART; // 自動(dòng)重啟被中斷的系統(tǒng)調(diào)用 sigaction(SIGCHLD, &sa, NULL); // 安裝信號(hào)處理程序 for (int i = 0; i < 3; i++) { pid_t pid = fork(); // 創(chuàng)建多個(gè)子進(jìn)程 if (pid == 0) { // 子進(jìn)程代碼 printf("Child process %d running...n", getpid()); sleep(2); exit(42); } } // 父進(jìn)程的其他工作 while (1) { printf("Parent process working...n"); sleep(1); } return 0;}
使用sigaction()的優(yōu)點(diǎn):
自動(dòng)重啟:通過(guò)設(shè)置SA_RESTART標(biāo)志,能夠在信號(hào)處理完成后自動(dòng)重啟被中斷的系統(tǒng)調(diào)用(如read()、write())。
可靠的信號(hào)處理:sigaction()避免了傳統(tǒng)signal()函數(shù)的缺陷,確保了信號(hào)處理的可靠性和可移植性。
SIGCHLD信號(hào)的常見(jiàn)問(wèn)題與解決方案:
丟失信號(hào):在同時(shí)終止多個(gè)子進(jìn)程時(shí),可能會(huì)丟失一些SIGCHLD信號(hào)。為解決這一問(wèn)題,可以在信號(hào)處理程序中循環(huán)調(diào)用waitpid(),直到?jīng)]有子進(jìn)程終止為止。
阻塞的系統(tǒng)調(diào)用:信號(hào)處理可能會(huì)中斷一些阻塞的系統(tǒng)調(diào)用(如read()或sleep()),導(dǎo)致它們返回錯(cuò)誤。通過(guò)使用sigaction()的SA_RESTART標(biāo)志可以自動(dòng)重啟被中斷的調(diào)用。
通過(guò)以上內(nèi)容,開(kāi)發(fā)者可以根據(jù)實(shí)際需求選擇合適的方法來(lái)監(jiān)視和管理子進(jìn)程,確保程序運(yùn)行的穩(wěn)定性和資源的有效利用。
如果只需等待任意一個(gè)子進(jìn)程終止且不關(guān)心特定子進(jìn)程,使用wait()是最簡(jiǎn)單的選擇。
如果需要非阻塞地等待特定子進(jìn)程或需要獲取更多子進(jìn)程狀態(tài)信息,waitpid()則更為靈活。
在處理多個(gè)子進(jìn)程時(shí),捕獲SIGCHLD信號(hào)可以讓父進(jìn)程更加實(shí)時(shí)地處理子進(jìn)程的終止,并在不中斷父進(jìn)程正常操作的情況下回收子進(jìn)程資源。
無(wú)論是使用wait()、waitpid()還是SIGCHLD信號(hào)處理,確保及時(shí)回收子進(jìn)程的資源是避免僵尸進(jìn)程的關(guān)鍵。
*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。