首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

深入淺出《Unix環境高級編程》筆記(三):程序與進程、PID 文件鎖、fork/wait 進程控制實戰

     編輯:Java 綜合教學

深入淺出《Unix環境高級編程》筆記(三):程序與進程、PID 文件鎖、fork/wait 進程控制實戰

繼續《Unix環境高級編程》(APUE)學習筆記系列。本章聚焦程序與進程的區別、進程標識 PID、進程控制基礎,附帶 C 語言實戰代碼演示 PID 文件防重啟與 fork/wait 父子進程協作。

核心概念釐清

關鍵點:同一程序可啟動多個進程,各自擁有獨立地址空間、文件描述符表、信號處理等。

進程標識 PID

  • 類型:pid_t(非負整數)。
  • 獲取:pid_t getpid(void) 返回調用進程 PID;pid_t getppid(void) 返回父進程 PID。
  • 唯一性:系統保證同一時刻無兩個進程 PID 相同;進程結束後 PID 可復用。

C 代碼:打印當前 PID

#include 
#include 
#include 

int main() {
    pid_t pid = getpid();
    printf("this process id is %d\n", pid);
    return 0;
}

編譯運行:

$ gcc -o pid pid.c
$ ./pid
this process id is 24047
$ ./pid
this process id is 24048  # 每次運行 PID 不同

實戰:PID 文件防止重複啟動

守護進程常用手段:啟動時檢查 .pid 文件是否存在,存在則退出;不存在則創建並寫入當前 PID,退出時刪除。

完整代碼

#include 
#include 
#include 
#include 
#include 
#include 

int main() {
    pid_t pid = getpid();
    char *pidfile = "./freesir.pid";
    char pids[16] = {0};
    int fd;

    // 1. 檢查 PID 文件是否存在
    if (access(pidfile, F_OK) != -1) {
        printf("this program is already run in system.\n");
        return -1;
    }

    // 2. 創建並寫入 PID
    fd = open(pidfile, O_TRUNC | O_CREAT | O_RDWR, 0644);
    if (fd < 0) {
        perror("open pid file error");
        return -2;
    }
    sprintf(pids, "%d", pid);
    if (write(fd, pids, strlen(pids)) != strlen(pids)) {
        perror("write pid file error");
        close(fd);
        return -3;
    }

    // 3. 模擬業務邏輯
    sleep(10);

    // 4. 清理
    unlink(pidfile);
    close(fd);
    printf("this process id is %d\n", pid);
    return 0;
}

注意:生產環境需考慮原子性(open(O_EXCL))、進程崩潰殘留 PID 文件(啟動時驗證 PID 是否存活)、權限等問題,推薦使用 flock/fcntl 文件鎖或成熟庫(如 libbsd 的 pidfile_open)。

進程控制:fork / waitpid

fork() 核心語義

  • 調用一次,返回兩次:父進程返回子進程 PID,子進程返回 0。
  • 子進程複製父進程地址空間(Copy-on-Write)、文件描述符表、信號處理等。
  • 失敗返回 -1,設置 errno。

waitpid() 等待子進程結束

pid_t waitpid(pid_t pid, int *status, int options);
// pid > 0: 等待指定 PID;pid = -1: 等待任意子進程
// status: 輸出子進程退出狀態,需用宏解析
// options: WNOHANG 非阻塞,WUNTRACED 報告停止狀態

三大狀態解析宏

概念定義比喻
程序存儲在磁盤上的可執行文件(靜態代碼+數據)停在車庫的汽車
進程程序加載入內存後的運行實例(動態、擁有資源)行駛在路上的汽車

父子進程協作示例

#include 
#include 
#include 
#include 

int main() {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork error");
        return 1;
    } else if (pid == 0) {           // 子進程
        printf("I'm child, pid=%d\n", getpid());
        sleep(5);
        exit(127);                   // 自定義退出碼
    } else {                         // 父進程
        printf("I'm parent, pid=%d, child_pid=%d\n", getpid(), pid);
        int status;
        pid_t w = waitpid(pid, &status, 0); // 阻塞等待
        if (w < 0) perror("waitpid error");
        else {
            if (WIFEXITED(status))
                printf("Child exited with code %d\n", WEXITSTATUS(status));
            else if (WIFSIGNALED(status))
                printf("Child killed by signal %d\n", WTERMSIG(status));
        }
    }
    return 0;
}

本章小結

  1. 程序 ≠ 進程:靜態文件 vs 動態實例,一對多關係。
  2. PID 是進程的身份證,getpid() 獲取,可用於防重啟鎖。
  3. fork() 是 Unix 進程創建的核心,「調用一次,返回兩次」是理解父子邏輯的鑰匙。
  4. waitpid() + 宏 是父進程感知子進程命運的標準方式,必須處理所有退出情況。

下一章將深入信號處理、管道通信、exec 族函數,敬請期待。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
宏用途
WIFEXITED(status)子進程正常退出(調用 exit/_exit)
WEXITSTATUS(status)取得正常退出碼(需先 WIFEXITED 為真)
WIFSIGNALED(status)子進程被信號終止
WTERMSIG(status)取得終止信號編號
WIFSTOPPED(status)子進程被停止(如 SIGSTOP)
WSTOPSIG(status)取得停止信號編號