首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Linux C 信號處理方法與實戰應用場景

     編輯:關於C語言

信號處理三大基本動作

動作函數/旗標行為
捕捉signal(sig, handler)
sigaction(sig, &act, NULL)
信號到達時執行自訂處理函數
忽略signal(sig, SIG_IGN)信號被丟棄,不影響進程
預設signal(sig, SIG_DFL)恢復系統預設動作(通常終止/核心傾印/停止/忽略)

特殊信號:無法捕捉/忽略

  • SIGKILL (9):無條件終止進程,給管理員最後手段
  • SIGSTOP (19):無條件停止進程,配合 SIGCONT 作業控制

這兩個信號直接由核心處理,不經過使用者空間處理函數,保證系統可控性。

信號處理的兩大需求

  1. 即時響應:錯誤通知(SIGSEGV、SIGFPE)、外部事件(SIGIO、SIGCHLD)
  2. 免打擾執行:關鍵區、即時運算、硬體存取——需暫時阻塞信號

實戰應用場景

場景一:錯誤偵測與優雅退出

volatile sig_atomic_t shutdown = 0;

void sig_handler(int sig) {
    shutdown = 1;  // 只設旗標,不做複雜操作
}

int main() {
    struct sigaction sa = {.sa_handler = sig_handler};
    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);

    while (!shutdown) {
        // 主迴圈工作
        do_work();
        // 定期檢查 shutdown,執行清理
    }
    cleanup();
    return 0;
}

場景二:大型科學運算進度報告

問題:在無窮迴圈中用 printf 輸出進度會嚴重拖慢效能。

解法:主迴圈全速運算,另開終端機送 SIGUSR1 觸發狀態輸出。

// 計算進程
volatile sig_atomic_t show_status = 0;

void status_handler(int sig) { show_status = 1; }

int main() {
    signal(SIGUSR1, status_handler);
    for (long i = 0; ; i++) {
        heavy_computation();
        if (show_status) {
            printf("Progress: %ld iterations\n", i);
            show_status = 0;
        }
    }
}

// 監控終端機:
// $ kill -USR1   # 即時查看進度,不影響運算效能

場景三:子進程回收避免殭屍

void sigchld_handler(int sig) {
    int saved_errno = errno;
    while (waitpid(-1, NULL, WNOHANG) > 0) {}
    errno = saved_errno;
}

int main() {
    struct sigaction sa = {.sa_handler = sigchld_handler, .sa_flags = SA_RESTART | SA_NOCLDSTOP};
    sigaction(SIGCHLD, &sa, NULL);
    // fork 子進程...
}

場景四:定時器驅動事件迴圈

// 參考 setitimer + sigsuspend 模式
// 實現毫秒級精度的非阻塞事件循環

信號處理函數編寫鐵律

規則說明
只呼叫 async-signal-safe 函數man 7 signal-safety 白名單:write、_exit、sigaction、kill 等
不使用 stdio (printf/malloc/free)非重入、鎖競爭、堆損壞
變數宣告為 volatile sig_atomic_t保證原子讀寫、防編譯器優化
儲存/恢復 errno避免中斷系統呼叫後 errno 被覆寫
處理函數極簡、快速返回避免阻塞其他信號遞送

現代替代方案:signalfd (Linux 2.6.22+)

#include <sys/signalfd.h>

sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sigprocmask(SIG_BLOCK, &mask, NULL);  // 必須先阻塞

int sfd = signalfd(-1, &mask, 0);
// 現在可用 poll/epoll 監聽 sfd,像處理檔案描述符一樣處理信號
// 讀取 signalfd_sinfo 結構取得信號資訊

優點:統一事件迴圈、無重入問題、可傳遞完整信號資訊(pid、uid、code 等)。

總結

信號處理核心在於「簡單可靠」:主迴圈負責複雜邏輯,處理函數只傳遞最小資訊。掌握 sigaction、sigprocmask、sigsuspend 三大核心 API,配合 SA_RESTART、volatile sig_atomic_t、signalfd 現代機制,即能構建健壯的 Linux 系統程式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved