Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Linux 系統程式設計:waitpid、fork 與 execlp 完整解析與實戰範例

編輯:Linux 教學

進程控制是 Linux 系統程式設計的核心技能。本文深度解析 waitpid、fork、execlp 三大核心系統呼叫,提供完整參數說明、返回值分析與可編譯執行的實戰程式碼。

waitpid:等待子進程狀態變化

函數原型

#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);

參數詳解

參數說明
pid>0:等待指定 PID;=0:等待同進程組任一子進程;=-1:等待任一子進程(等同 wait());<-1:等待指定進程組 ID(絕對值)
status輸出參數,儲存子進程退出狀態,可為 NULL
optionsWNOHANG:無子進程結束立即返回不阻塞;WUNTRACED:子進程暫停也返回

狀態判斷巨集

WIFEXITED(status)    // 正常結束
WEXITSTATUS(status)  // 取得 exit code(需先判斷 WIFEXITED)
WIFSIGNALED(status)  // 因信號終止
WTERMSIG(status)     // 取得終止信號編號
WIFSTOPPED(status)   // 處於暫停狀態
WSTOPSIG(status)     // 取得導致暫停的信號編號

實戰範例:父進程正確等待子進程

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    pid_t childpid;
    int status;
    
    childpid = fork();
    if (childpid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    else if (childpid == 0) {  // 子進程
        puts("In child process");
        sleep(3);
        printf("\tchild pid = %d\n", getpid());
        printf("\tchild ppid = %d\n", getppid());
        exit(EXIT_SUCCESS);
    }
    else {  // 父進程
        waitpid(childpid, &status, 0);  // 阻塞等待
        puts("in parent");
        printf("\tparent pid = %d\n", getpid());
        printf("\tparent ppid = %d\n", getppid());
        printf("\tchild process exited with status %d\n", status);
    }
    return 0;
}

關鍵觀察:若註解掉 waitpid,父進程會先於子進程結束,子進程成為「孤兒進程」被 init(PID 1)收養,ppid 變為 1。

fork:建立子進程

函數原型

#include <unistd.h>
pid_t fork(void);

核心特性

  • 「調用一次,返回兩次」:父進程返回子 PID,子進程返回 0,出錯返回 -1
  • 子進程獲得父進程資料段、堆、棧的副本(Copy-on-Write 機制),僅共享程式碼段
  • 檔案描述符表被複製,父子共享同一檔案表項(檔案偏移量共享)

基本骨架

pid_t pid = fork();
if (pid < 0) { /* 錯誤處理 */ }
else if (pid == 0) { /* 子進程邏輯 */ }
else { /* 父進程邏輯,pid 為子進程 PID */ }

execlp:從 PATH 搜尋並執行程式

函數原型

#include <unistd.h>
int execlp(const char *file, const char *arg, ... /* (char *)NULL */);

關鍵行為

  • 從 PATH 環境變數目錄搜尋 file
  • 參數列表:arg0(通常為程式名)、arg1...,必須以 (char *)NULL 結尾
  • 成功不返回,新程式映像取代當前進程映像(PID 不變);失敗返回 -1

範例:執行 ls -al /etc/passwd

execlp("ls", "ls", "-al", "/etc/passwd", (char *)0);

組合實戰:fork + execlp + waitpid 實作簡易 Shell

#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_ARGS 64

int main(void) {
    char line[1024];
    char *argv[MAX_ARGS];
    
    while (1) {
        printf("mysh> ");
        if (!fgets(line, sizeof(line), stdin)) break;
        line[strcspn(line, "\n")] = 0;  // 移除換行
        
        if (strcmp(line, "exit") == 0) break;
        if (strlen(line) == 0) continue;
        
        // 簡易參數解析
        int argc = 0;
        char *token = strtok(line, " ");
        while (token && argc < MAX_ARGS - 1) {
            argv[argc++] = token;
            token = strtok(NULL, " ");
        }
        argv[argc] = NULL;
        
        pid_t pid = fork();
        if (pid == 0) {
            execvp(argv[0], argv);  // 使用 execvp 自動搜尋 PATH
            perror("execvp");
            exit(EXIT_FAILURE);
        }
        else if (pid > 0) {
            int status;
            waitpid(pid, &status, 0);
            printf("[退出碼: %d]\n", WEXITSTATUS(status));
        }
        else {
            perror("fork");
        }
    }
    return 0;
}

常見陷阱與最佳實踐

陷阱解決方案
忘記檢查 fork 返回值必須處理 -1 錯誤情況
父進程未 wait 導致僵屍進程使用 waitpid 或設置 SIGCHLD 處理器
execlp 參數未以 NULL 結尾務必加 (char *)NULL
exec 失誤後未退出子進程exec 失敗時必須 exit() 或 _exit()
混淆 fork/vforkvfork 共享位址空間,極度危險,現代建議避免使用

掌握 fork → exec → waitpid 經典三部曲,是編寫健壯 Linux 系統程式、Shell、服務管理器的基石。

Copyright © Windows教程網 All Rights Reserved