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

Linux 多線程信號投遞機制深度解析:信號究竟發給誰?

     編輯:關於C語言

核心結論

  • 進程內部產生的信號(raise()、硬件異常如 SIGSEGV、pthread_kill())→ 發送給產生信號的那個線程。
  • 進程外部產生的信號(終端 Ctrl+C/Ctrl+\, kill()、sigqueue())→ 發送給主線程(或未屏蔽該信號的任意線程,內核選擇)。

實驗環境

OS: Fedora 11, Kernel 2.6.29, GCC 4.4.0
編譯: gcc -g -W -Wall -Wextra -o mytest main.c -lpthread

實驗一:終端產生信號 → 主線程

// 主線程註冊 SIGQUIT (Ctrl+\)
// 子線程註冊 SIGINT  (Ctrl+C)
// 兩線程均 pause() 等待
// 結果:無論按 Ctrl+C 還是 Ctrl+\,處理函數均在主線程執行(TID=主線程ID)

外部信號(終端發送給進程組)由內核投遞到主線程(或未屏蔽該信號的線程)。

實驗二:硬件異常信號 → 故障線程

// 主線程註冊 SIGSEGV 處理器
// 子線程循環 sleep(2) 並故意 *p = 'a' 觸發 SIGSEGV
// 結果:SIGSEGV 處理器在子線程執行

硬件異常(SIGSEGV、SIGFPE、SIGILL、SIGBUS)同步產生於故障指令所在線程,精確投遞給該線程。

實驗三:raise() 產生信號 → 調用線程

// 主線程註冊 SIGQUIT
// 子線程循環 raise(SIGQUIT)
// 結果:SIGQUIT 處理器在子線程執行

raise() 調用 pthread_kill(pthread_self(), sig),精確投遞給調用線程。

內核機制原理

  • 每個線程有獨立的信號屏蔽字 (pthread_sigmask) 和待處理信號集。
  • 進程級待處理信號集(共享)+ 線程級待處理信號集(私有)。
  • 外部信號 → 進程級集合 → 內核選擇一個未屏蔽該信號的線程投遞(通常優先主線程)。
  • 同步信號(硬件異常、raise、pthread_kill)→ 直接放入目標線程私有集合,精確投遞。

工程最佳實踐

  1. 主線程統一設置信號處理器,其他線程屏蔽所有異步信號(pthread_sigmask(SIG_BLOCK, &set, NULL))。
  2. 專用信號處理線程:創建一個線程調用 sigwait() 同步等待信號,將異步信號轉為同步處理,避免信號處理函數的限制(只能調用 async-signal-safe 函數)。
  3. sigaction 設置 SA_SIGINFO 獲取 siginfo_t,可知 si_pid/si_uid 判斷信號來源。
  4. 避免在信號處理函數中使用鎖、分配內存、I/O 操作。

代碼模板:專用信號線程

void *signal_thread(void *arg) {
    sigset_t set;
    sigfillset(&set);
    pthread_sigmask(SIG_BLOCK, &set, NULL); // 繼承屏蔽集
    for (;;) {
        int sig;
        sigwait(&set, &sig); // 同步等待
        handle_signal(sig);  // 安全上下文中處理
    }
    return NULL;
}

int main() {
    sigset_t set;
    sigfillset(&set);
    pthread_sigmask(SIG_BLOCK, &set, NULL); // 主線程及後續線程繼承屏蔽
    pthread_t tid;
    pthread_create(&tid, NULL, signal_thread, NULL);
    // 業務邏輯...
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved