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 信號機制深度解析:概念、類型、處理與系統調用

編輯:Linux 教學

前言

信號 是 Linux 進程間異步通信的核心機制,亦是內核通知進程發生異步事件 (硬體異常、終端操作、定時器、用戶請求) 的唯一途徑。本文系統梳理信號基本概念、完整信號表、處理流程、核心系統調用與實戰編程模式。

一、信號基本概念

1. 定義與特性

  • 軟中斷信號:模擬硬體中斷,屬「異步」事件,進程無法預知何時到達。
  • 僅通知,不傳遞數據:標準信號不攜帶負載 (實時信號 `SIGRTMIN+` 可攜帶 `siginfo_t`)。
  • 生命週期:產生 → 未決 → 遞達 → 處理。若進程阻塞該信號,滯留於「未決集」直到解除阻塞或忽略。
  • 進程層面:每進程維护「信號屏蔽字」 (阻塞集) 與「未決信號集」;`task_struct->signal` 共享線程組信號狀態。

2. 三種處理動作

二、Linux 信號完整對照表 (x86_64)

動作描述系統調用
捕獲註冊處理函數,發生時執行用戶代碼signal(), sigaction()
忽略丟棄信號,如未發生 (SIGKILL/SIGSTOP 不可忽略)signal(SIG_IGN)
默認內核預設動作 (Term/Core/Stop/Ignore)signal(SIG_DFL)
信號值默認動作來源/含義備註
SIGHUP1Term終端掛起/控制進程終止守護進程常用於重讀配置
SIGINT2Term鍵盤中斷Ctrl+C
SIGQUIT3Core鍵盤退出鍵Ctrl+\ 產生 core dump
SIGILL4Core非法指令
SIGTRAP5Core斷點/跟蹤陷阱調試器使用
SIGABRT6Coreabort() 調用
SIGBUS7Core總線錯誤 (非對齊訪問)
SIGFPE8Core浮點異常 (除零等)
SIGKILL9Term無條件終止不可捕獲/忽略/阻塞
SIGUSR110Term用戶自定義 1應用層協議
SIGSEGV11Core無效內存引用段錯誤
SIGUSR212Term用戶自定義 2
SIGPIPE13Term管道破裂 (寫無讀端)網路編程常見
SIGALRM14Termalarm() 定時器
SIGTERM15Term優雅終止請求systemd stop 默認發送
SIGSTKFLT16Term協處理器堆棧錯誤較少見
SIGCHLD17Ign子進程結束/停止需 wait() 回收避免殭屍
SIGCONT18Cont繼續執行 (被停止進程)
SIGSTOP19Stop無條件停止不可捕獲/忽略/阻塞
SIGTSTP20Stop終端停止鍵Ctrl+Z
SIGTTIN21Stop後台進程讀終端
SIGTTOU22Stop後台進程寫終端
SIGURG23IgnSocket 緊急數據帶外數據
SIGXCPU24CoreCPU 時間超限setrlimit(RLIMIT_CPU)
SIGXFSZ25Core文件大小超限setrlimit(RLIMIT_FSIZE)
SIGVTALRM26Term虛擬定時器setitimer(ITIMER_VIRTUAL)
SIGPROF27Term剖析定時器setitimer(ITIMER_PROF)
SIGWINCH28Ign終端窗口大小變化終端程序處理
SIGIO29TermI/O 可用 (異步 I/O)SIGPOLL 同義
SIGPWR30Term電源故障UPS 通知
SIGSYS31Core無效系統調用seccomp 觸發
SIGRTMIN ~ SIGRTMAX32~64Term實時信號 (排隊、攜帶數據)應用層定義

動作代碼:Term=終止、Core=終止並產生核心轉儲、Stop=停止、Cont=繼續、Ign=忽略。

三、核心系統調用

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

#include 
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
  • 語義隨 Unix 版本差異 (System V vs BSD);**新代碼請使用 `sigaction()`**。
  • 返回值:前一處理函數指針,或 `SIG_ERR`。

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

#include 
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;                   // SA_RESTART, SA_SIGINFO, SA_NOCLDSTOP...
    void     (*sa_restorer)(void);         // 內部使用,忽略
};

關鍵 Flags

  • `SA_RESTART`:被中斷的系統調用 (read, write, accept 等) 自動重啟,而非返回 EINTR。
  • `SA_SIGINFO`:使用 `sa_sigaction` 獲取 `siginfo_t` (發送者 PID、UID、地址等) 與 `ucontext_t`。
  • `SA_NOCLDSTOP`:子進程停止/繼續不產生 SIGCHLD (僅針對 SIGCHLD)。
  • `SA_NODEFER`:處理函數執行期間不自動阻塞自身信號 (默認阻塞)。
  • `SA_RESETHAND`:處理一次後恢復默認動作 (一次性捕獲)。

3. 信號集操作

#include 
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);

4. 阻塞/解除阻塞/查詢未決

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
// how: SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK

int sigpending(sigset_t *set);  // 讀取未決信號集

5. 發送信號

int kill(pid_t pid, int signum);      // 進程/進程組
int raise(int signum);                // 當前進程
int sigqueue(pid_t pid, int signum, const union sigval value); // 實時信號攜帶數據

6. 同步等待信號

int sigsuspend(const sigset_t *mask);  // 原子地設置 mask 並掛起,捕獲信號返回
int sigwait(const sigset_t *set, int *sig); // 同步等待集合中任一信號 (多線程專用)
int sigwaitinfo(const sigset_t *set, siginfo_t *info); // 同步等待並獲取詳細信息

四、實戰編程模式

1. 標準信號捕獲範例

void handler(int sig) {
    // 僅設置標誌,非 async-signal-safe 函數不可調用
    volatile sig_atomic_t got_sigterm = 1;
}

int main() {
    struct sigaction sa = {0};
    sa.sa_handler = handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;  // 關鍵:避免慢速系統調用返回 EINTR
    sigaction(SIGTERM, &sa, NULL);
    sigaction(SIGINT, &sa, NULL);

    while (!got_sigterm) {
        // 主循環
        pause();  // 或 sleep、select、epoll_wait
    }
    // 清理資源後退出
    return 0;
}

2. SIGCHLD 正確回收子進程

void sigchld_handler(int sig) {
    int saved_errno = errno;
    while (waitpid(-1, NULL, WNOHANG) > 0) {}  // 循環回收所有已終止子進程
    errno = saved_errno;
}

sigaction(SIGCHLD, &(struct sigaction){.sa_handler=sigchld_handler, .sa_flags=SA_RESTART|SA_NOCLDSTOP}, NULL);

3. 實時信號傳遞數據

// 發送端
union sigval val;
val.sival_int = 42;
sigqueue(pid, SIGRTMIN+1, val);

// 接收端 (需 SA_SIGINFO)
void rt_handler(int sig, siginfo_t *info, void *ctx) {
    printf("收到實時信號 %d, 數據: %d, 發送者 PID: %d\n", sig, info->si_value.sival_int, info->si_pid);
}
sigaction(SIGRTMIN+1, &(struct sigaction){.sa_sigaction=rt_handler, .sa_flags=SA_SIGINFO}, NULL);

五、Async-Signal-Safe 函數清單 (處理函數內可安全調用)

僅調用 POSIX 保證安全的函數:`write`、`_exit`、`sigaction`、`sigprocmask`、`sigemptyset`、`sigaddset`、`sigdelset`、`sigismember`、`kill`、`raise`、`alarm`、`pause`、`sleep`、`abort`、`getpid`、`getppid` 等。**嚴禁調用 `printf`、`malloc`、`free`、`lock`、非重入庫函數**。

結語

信號是 Linux 異步編程的基石。掌握 `sigaction` + `SA_RESTART` + `sigprocmask` + `sigsuspend/sigwaitinfo` 組合拳,遵循「處理函數極簡、僅設置標誌、主循環統一處理」原則,能構建健壯的服務器、守護進程、容器管理器等系統級軟體。實時信號 (SIGRT) 進一步解決了標準信號不排隊、無數據的限制,適用於高併發事件通知場景。

Copyright © Windows教程網 All Rights Reserved