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 信號機制深度解析:signal、sigaction、信號遮罩、重入安全與實戰模式

編輯:關於 Linux

信號是 Linux 進程間通訊(IPC)最基礎的非同步機制,亦稱「軟中斷」。本文從核心資料結構、signal() vs sigaction()、信號遮罩、SA_RESTART、非同步信號安全函數、多執行緒信號語義等維度,系統解析 Linux 信號機制。

信號處理流程

一、信號基礎概念

常見信號速查

SIGHUP(1)   終端機掛斷/重讀配置
SIGINT(2)   Ctrl+C 終止
SIGQUIT(3)  Ctrl+\ 核心轉儲
SIGKILL(9)  強制終止(不可捕捉)
SIGTERM(15) 優雅終止(預設 kill)
SIGCHLD(17) 子進程結束
SIGUSR1(10) 使用者自訂 1
SIGUSR2(12) 使用者自訂 2

二、安裝信號處理函數:signal() vs sigaction()

1. signal() — 簡單但不可移植

sighandler_t signal(int signum, sighandler_t handler);
// 回傳前一個處理函數,失敗回傳 SIG_ERR
  • 語法簡單,但行為在 System V、BSD、POSIX 間有差異
  • 不支援信號遮罩、旗標控制、取得舊處理器而不安裝新處理器
  • 不建議在可移植程式中使用

2. sigaction() — 標準、功能完整、可移植

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

struct sigaction {
    void     (*sa_handler)(int);           // 傳統處理函數
    void     (*sa_sigaction)(int, siginfo_t*, void*); // 擴充處理函數(SA_SIGINFO)
    sigset_t sa_mask;                      // 處理期間阻塞的信號集
    int      sa_flags;                     // 行為旗標
    void     (*sa_restorer)(void);         // 過時欄位,勿用
};

關鍵旗標 sa_flags

屬性說明
非同步隨時可能打斷進程正常流程
編號範圍1-31(標準信號),34-64(即時信號,Linux 特有)
預設動作Term(終止)、Core(終止+核心轉儲)、Ign(忽略)、Stop(停止)、Cont(繼續)
不可阻塞/捕捉SIGKILL(9)、SIGSTOP(19)

sigaction() 完整範例

#include 
#include 
#include 

void handler(int sig) {
    printf("Received signal: %s\n", strsignal(sig));
}

int main() {
    struct sigaction act = {0};
    sigemptyset(&act.sa_mask);      // 清空遮罩
    act.sa_handler = handler;
    act.sa_flags = SA_RESTART;      // 系統呼叫自動重啟
    
    if (sigaction(SIGUSR1, &act, NULL) == -1) {
        perror("sigaction");
        return 1;
    }
    
    while (1) pause();  // 等待信號
    return 0;
}

三、信號遮罩與阻塞控制

// 信號集操作
sigset_t set;
sigemptyset(&set);      // 清空
sigfillset(&set);       // 全部
sigaddset(&set, SIGUSR1);  // 加入
sigdelset(&set, SIGUSR1);  // 移除
sigismember(&set, SIGUSR1); // 檢查

// 進程信號遮罩(阻塞/解除阻塞)
sigprocmask(SIG_BLOCK, &set, &oldset);   // 阻塞
sigprocmask(SIG_UNBLOCK, &set, &oldset); // 解除
sigprocmask(SIG_SETMASK, &set, &oldset); // 設定

// 臨時解除阻塞等待信號
sigsuspend(&mask);  // 原子性:解除阻塞 + 等待 + 恢復阻塞

四、非同步信號安全函數

信號處理函數中只能呼叫 async-signal-safe 函數,否則導致未定義行為(死鎖、資料損毀)。

常見安全函數(man 7 signal-safety)

_exit, write, read, open, close, sigaction, sigprocmask,
sigemptyset, sigfillset, sigaddset, sigdelset, sigismember,
kill, raise, alarm, pause, sleep, getpid, getuid, time,
strsignal, psignal(部分配置)

⚠️ 絕對不可呼叫

printf, malloc, free, fopen, fclose, pthread_mutex_lock,
pthread_cond_signal, syslog, getpwnam, inet_ntoa, 
所有非重入函數、使用全域狀態的函數

正確模式:信號處理器只設旗標,主迴圈處理邏輯

volatile sig_atomic_t got_sigusr1 = 0;

void handler(int sig) {
    got_sigusr1 = 1;  // 僅設旗標
}

int main() {
    // 安裝 handler...
    while (1) {
        if (got_sigusr1) {
            got_sigusr1 = 0;
            // 安全執行複雜邏輯:printf、malloc、DB 操作等
            do_complex_work();
        }
        // 其他工作...
    }
}

五、多執行緒與信號

  • 信號送達給進程,由核心選擇未阻塞該信號的任一執行緒處理
  • 建立執行緒時繼承進程信號遮罩(pthread_sigmask 可單獨設定)
  • 典型模式:專用一條執行緒 sigwait() 同步處理信號
// 專用信號處理執行緒
void* signal_thread(void* arg) {
    sigset_t set;
    sigfillset(&set);
    pthread_sigmask(SIG_BLOCK, &set, NULL);  // 阻塞所有信號
    
    while (1) {
        int sig;
        sigwait(&set, &sig);  // 同步等待
        handle_signal(sig);   // 安全處理
    }
}

int main() {
    sigset_t set;
    sigfillset(&set);
    pthread_sigmask(SIG_BLOCK, &set, NULL);  // 主執行緒阻塞所有
    
    pthread_t tid;
    pthread_create(&tid, NULL, signal_thread, NULL);
    // 主執行緒專心業務邏輯
}

六、SA_RESTART 與系統呼叫中斷

旗標效果
SA_RESTART被信號中斷的系統呼叫自動重啟(如 read、accept)
SA_SIGINFO使用 sa_sigaction 取得 siginfo_t 詳細資訊
SA_NOCLDSTOP子進程停止不產生 SIGCHLD
SA_NOCLDWAIT子進程結束不產生殭屍、不發 SIGCHLD
SA_NODEFER處理期間不自動阻塞同一信號(預設會阻塞)
SA_RESETHAND處理後重置為預設動作(一次性)
SA_ONSTACK使用替代信號堆疊(sigaltstack)

七、實戰模式總結

  1. 一律使用 sigaction(),設定 SA_RESTART
  2. 處理器極簡:僅設 volatile sig_atomic_t 旗標或 write() 到 self-pipe
  3. 複雜邏輯在主迴圈 輪詢旗標或 select/poll/epoll 監聽 self-pipe
  4. 多執行緒:專用 sigwait() 執行緒,主執行緒阻塞所有信號
  5. 啟動時 先 sigfillset + pthread_sigmask(SIG_BLOCK),再建立執行緒

精通信號機制,是編寫健壯 Linux 系統程式、高效能網路伺服器、容器化應用的核心基本功。

Copyright © Windows教程網 All Rights Reserved
系統呼叫無 SA_RESTART有 SA_RESTART
read, write回傳 -1, errno=EINTR自動重啟
accept, recv回傳 -1, errno=EINTR自動重啟
select, poll回傳 -1, errno=EINTR自動重啟
sleep回傳剩餘秒數自動重啟
wait, waitpid回傳 -1, errno=EINTR自動重啟
connect回傳 -1, errno=EINTR不重啟(連線建立不可重入)