使用 C 語言中的 waitpid 函式

Jinku Hu 2023年10月12日
  1. 在 C 語言中使用 waitpid 函式監控子程序狀態
  2. 在 C 語言中使用巨集來顯示子程序的等待狀態
使用 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 <sys/wait.h>
#include <unistd.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 呼叫時帶有 WUNTRACEDWCONTINUED 引數,這意味著父程序通過相應的訊號來監控子程序是否被停止或繼續。另外,我們還實現了 printWaitStatus 函式,可以呼叫它來列印檢索到的子程式狀態。注意,它是使用 <sys/wait.h> 頭中定義的 W*型別巨集,從 status 整數變數中提取編碼資訊。

由於不是所有的巨集在所有的平臺上都可以使用,所以有一些條件性的預處理程式定義,從而保證了函式的可移植性,無論如何都能編譯成功。

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.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);
}
作者: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

DelftStack.com 創辦人。Jinku 在機器人和汽車行業工作了8多年。他在自動測試、遠端測試及從耐久性測試中創建報告時磨練了自己的程式設計技能。他擁有電氣/ 電子工程背景,但他也擴展了自己的興趣到嵌入式電子、嵌入式程式設計以及前端和後端程式設計。

LinkedIn Facebook

相關文章 - C Process