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

C語言進程間通信:signal 信號處理與 kill 發送,父子進程同步實戰

     編輯:關於C

信號是進程間通信(IPC)中最古老、最輕量的機制:異步通知,攜帶極少信息(僅信號編號)。本文演示 fork() 父子進程中,父進程用 kill() 發送 SIGALRM,子進程用 signal() 註冊處理函數接收,並深入剖析 signal() 的競態缺陷:處理函數註冊順序導致信號丟失。

核心 API

#include 

// 發送信號
int kill(pid_t pid, int signo);
// pid: 目標進程 PID;signo: 信號編號(如 SIGALRM=14)

// 註冊處理函數(舊接口,不推薦新代碼使用)
void (*signal(int signo, void (*handler)(int)))(int);
// 返回前一個處理函數指針

完整代碼:父發送、子接收

#include 
#include 
#include 
#include 

void sig_receive(int signo) {
    printf("Received signal: %d\n", signo);
}

int main() {
    pid_t pid, pid_res;
    int stat_value;

    pid = fork();
    if (pid < 0) { perror("fork"); return 1; }

    if (pid > 0) {  // 父進程:發送
        printf("PID: %d -> Father sends signal\n", getpid());
        kill(pid, SIGALRM);  // 發送 SIGALRM (14)
    } else {  // 子進程:註冊處理並接收
        signal(SIGALRM, sig_receive);  // 註冊處理函數
        printf("PID: %d -> Son waits for signal\n", getpid());
        // 子進程這裡可 pause() 等待,或繼續執行
    }

    pid_res = wait(&stat_value);
    if (pid_res > 0) {
        printf("Son finished: PID = %d\n", pid_res);
    }
    return 0;
}

正常運行結果

PID: 3208 -> Father sends signal
Received signal: 14
PID: 3209 -> Son waits for signal
Son finished: PID = 3209

致命缺陷:註冊順序導致信號丟失

若子進程先打印再註冊:

    } else {
        printf("PID: %d -> Son waits for signal\n", getpid());  // 先打印
        signal(SIGALRM, sig_receive);                           // 後註冊
    }

異常運行結果

PID: 2611 -> Father sends signal
Son finished: PID = 2612

現象:子進程未打印「Received signal」,直接結束。信號在 printf 到 signal() 間隙到達,默認動作(終止進程)生效,處理函數未來得及註冊。

signal() 競態根因

解決方案對比

階段說明
1. 父進程 kill信號投遞到子進程內核結構
2. 子進程執行 printf用戶態運行,信號待處理
3. 信號到達內核檢查處理函數:尚未註冊 → 執行默認動作(終止)
4. 子進程執行 signal()已太晚,進程即將退出
方案代碼優缺點
調整順序:先註冊後業務signal(...); printf(...);簡單,但不可靠(多線程/複雜邏輯仍有窗口)
用 sigaction + sa_masksigaction(SIGALRM, &act, NULL); 並 sigaddset(&act.sa_mask, SIGALRM)原子註冊,屏蔽集防嵌套,專業標準
子進程 pause() 等待signal(...); pause();同步等待,但註冊前窗口仍存在
父進程 sleep 後發送sleep(1); kill(...);極其不可靠,絕不可用於生產

總結

signal() 是教學/兼容用接口,核心缺陷在於註冊與生效非原子,存在「信號丟失/默認動作」競態。實際項目必須使用 sigaction():

  1. 結構體配置原子生效。
  2. sa_mask 防止處理期間同信號遞歸/嵌套。
  3. 支持 SA_SIGINFO 獲取發送者 PID、用戶數據。

本文代碼僅為演示信號 IPC 基本形態,**生產代碼請直接參考《sigaction 替代 signal》篇**。

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