使用 C 語言中的 waitpid 函式
本文將演示關於如何使用 C 語言中的 waitpid
函式的多種方法。
在 C 語言中使用 waitpid
函式監控子程序狀態
在基於 Unix 的系統中,有一個程序的概念,它只是一個程式的執行例項。該程序可以使用 fork
系統呼叫建立其他程序,並執行給定的部分程式碼。需要注意的是,在本專題中,系統呼叫是以 C 式函式的形式提供給使用者的作業系統服務。一般來說,在很多場景下,程式需要監視它建立的稱為子程序的程序。wait
函式族就是提供這種功能的,waitpid
就是其中之一。
wait
系統呼叫有多種限制,為了覆蓋更高階的功能,需要利用 waitpid
函式。即,如果一個程序建立了多個子程序,而父程序需要監控某個特定的子程序,只有 waitpid
可以做到這一點。在下面的例子中,我們實現了一個名為 spawnChild
的函式,它可以建立一個新的子程序,並執行不同的程式。為了很好地演示這個例子,我們正在執行一個 top
程式(幾乎所有基於 Unix 的系統都有),這個程式一直執行到使用者終止它。一旦函式在父程序中返回,我們就儲存一個子程序的 ID,並將其傳遞給 waitpid
函式來監視狀態。
waitpid
需要三個引數,其中第一個引數是程序 PID 號(pid)。pid
可以有多個預先指定的值,並有不同的效果,但在本例中,我們只提 -1
和 >0
。-1
值可以傳遞給監控任何先改變其狀態的子程序,它用來實現 wait
功能。>0
意味著該值應該是由 fork
函式返回的實際程序 ID,而這個 ID 又只用來監視特定的子程序。第二個引數的型別是 int
指標,我們應該宣告一個整數變數,將其地址傳遞給函式。而 waitpid
則會將子程序的狀態資訊儲存在給定的 int
變數中,然後可以使用預定義的巨集進行解碼。最後一個引數的型別是 int
,除了預設的事件外,它還用來指定要監控的某些子程序事件。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
pid_t spawnChild(const char* program, char** arg_list)
{
pid_t ch_pid = fork();
if (ch_pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (ch_pid > 0) {
printf("spawn child with pid - %d\n", ch_pid);
return ch_pid;
} else {
execvp(program, arg_list);
perror("execve");
exit(EXIT_FAILURE);
}
}
int main(void) {
const char *args[] = { "top", NULL, NULL };
pid_t child;
int wstatus;
child = spawnChild("top", args);
if (waitpid(child, &wstatus, WUNTRACED | WCONTINUED) == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
在 C 語言中使用巨集來顯示子程序的等待狀態
需要注意的是,當 waitpid
函式被呼叫時,父程序是被暫停的,直到被監控的子程序改變狀態,它才會恢復執行。下一個例子顯示了 waitpid
呼叫時帶有 WUNTRACED
和 WCONTINUED
引數,這意味著父程序通過相應的訊號來監控子程序是否被停止或繼續。另外,我們還實現了 printWaitStatus
函式,可以呼叫它來列印檢索到的子程式狀態。注意,它是使用 <sys/wait.h>
頭中定義的 W*
型別巨集,從 status
整數變數中提取編碼資訊。
由於不是所有的巨集在所有的平臺上都可以使用,所以有一些條件性的預處理程式定義,從而保證了函式的可移植性,無論如何都能編譯成功。
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
pid_t spawnChild(const char* program, char** arg_list)
{
pid_t ch_pid = fork();
if (ch_pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (ch_pid > 0) {
printf("spawn child with pid - %d\n", ch_pid);
return ch_pid;
} else {
execvp(program, arg_list);
perror("execve");
exit(EXIT_FAILURE);
}
}
void printWaitStatus(int status)
{
if (WIFEXITED(status)) {
printf("child exited, status=%d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("child killed by signal %d (%s)",
WTERMSIG(status), strsignal(WTERMSIG(status)));
#ifdef WCOREDUMP
if (WCOREDUMP(status))
printf(" (core dumped)");
#endif
printf("\n");
} else if (WIFSTOPPED(status)) {
printf("child stopped by signal %d (%s)\n",
WSTOPSIG(status), strsignal(WSTOPSIG(status)));
#ifdef WIFCONTINUED
} else if (WIFCONTINUED(status)) {
printf("child continued\n");
#endif
} else {
printf("status=%x\n",
(unsigned int) status);
}
}
int main(void) {
const char *args[] = { "top", NULL, NULL };
pid_t child;
int wstatus;
child = spawnChild("top", args);
if (waitpid(child, &wstatus, WUNTRACED | WCONTINUED) == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
printWaitStatus(wstatus);
exit(EXIT_SUCCESS);
}
Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.
LinkedIn