Linux系統:C語言進程間通信信號(Signal)

1. 引言:從"中斷"到"信號"

想象一下,你正在書房專心致志地寫代碼,這時廚房的水燒開了,鳴笛聲大作。你會怎么做?你會暫停(Interrupt)?手頭的工作,跑去廚房關掉燒水壺,然后再回來繼續 coding。

在Linux系統中,信號(Signal)?就是一種類似的異步中斷機制。它允許一個進程(或內核)向另一個進程發送一個簡單的消息,通知其某個特定事件的發生。接收信號的進程通常會暫停當前正在執行的指令流,轉而去執行一個特殊的信號處理函數,處理完畢后(如果沒退出)再回來繼續執行。這就是信號最基本的概念。

本文將深入探討信號的產生、處理以及如何利用它來構建一個簡單的音樂播放器控制器。

2. 進程間通信(IPC)與信號概述

進程是操作系統資源分配和獨立運行的基本單位。每個進程都擁有自己獨立的地址空間,一個進程無法直接訪問另一個進程的數據。因此,進程之間需要一種機制來進行通信(Communication)?與同步(Synchronization),這就是進程間通信(IPC, Inter-Process Communication)

常見的IPC方式包括:

  • 信號(Signal): 本文焦點,一種異步的、簡單的通知機制。

  • 管道(Pipe)?/?命名管道(FIFO): 單向或雙向的字節流通信。

  • 套接字(Socket): 功能最強大,可用于網絡通信和不同主機間的進程通信。

  • IPC對象: 包括共享內存信號量集消息隊列,源自System V IPC標準。

信號是其中最輕量、最古老的一種方式。它攜帶的信息量很小,通常只是一個信號編號,但其響應非常迅速。

3. 信號的深度解析

3.1 信號列表與分類

在Linux系統中,可以使用?kill -l?命令查看所有支持的信號。

$ kill -l1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
...

信號可分為兩大類:

  • 不可靠信號(1 ~ 31): 源于UNIX早期版本,也稱為非實時信號。它們可能會丟失。如果同一個不可靠信號在短時間內多次產生,進程可能只能接收到一次。因為內核可能使用位圖來記錄它們的發生,多次相同的信號在處理之前會被合并為一次。

  • 可靠信號(34 ~ 64): 在POSIX.1標準中定義,也稱為實時信號。它們支持排隊,只要信號發送的速度不超過系統隊列的上限,信號就不會丟失。

3.2 信號的產生方式

信號的產生源頭多種多樣:

  1. 用戶終端

    • Ctrl + C?-> 產生?SIGINT?(Interrupt) 信號,通常用于終止前臺進程。

    • Ctrl + \?-> 產生?SIGQUIT?(Quit) 信號,不僅終止進程,還會生成core dump文件。

    • Ctrl + Z?-> 產生?SIGTSTP?(Terminal Stop) 信號,暫停前臺進程。

  2. 系統命令

    • kill -SIGNO PID: 向指定PID的進程發送信號。kill -9 1234?是強制殺死進程1234的經典命令。

  3. 硬件異常

    • 進程執行了非法操作,如訪問非法內存(段錯誤) -> 內核會向其發送?SIGSEGV

    • 執行了錯誤的算術運算(如除以0) -> 內核會向其發送?SIGFPE

  4. 軟件事件

    • 子進程退出時,內核會向其父進程發送?SIGCHLD

    • 由?alarm?或?setitimer?設置的定時器超時后,會發送?SIGALRM

3.3 核心API函數詳解

3.3.1?kill()?- 發送信號
#include <sys/types.h>
#include <signal.h>int kill(pid_t pid, int sig);
  • 功能: 向指定進程(或進程組)發送一個信號。

  • 參數

    • pid?> 0: 目標進程的PID。

    • pid?== 0: 發送給與調用進程同進程組的所有進程。

    • pid?== -1: 發送給所有有權限發送的進程(除init進程外)。

    • sig: 要發送的信號編號,如?SIGINT,?SIGKILL

  • 返回值: 成功返回0,失敗返回-1并設置errno。

3.3.2?raise()?- 給自己發信號
#include <signal.h>int raise(int sig);

功能:?kill(getpid(), sig)?的簡化版,向當前進程自身發送信號。

  • 參數:?sig?- 信號編號。

3.3.3?alarm()?- 設置鬧鐘
#include <unistd.h>unsigned int alarm(unsigned int seconds);
  • 功能: 設置一個定時器(鬧鐘),在?seconds?秒后,內核會向當前進程發送?SIGALRM?信號。該信號的默認動作是終止進程。

  • 特點:?重置性。如果一個進程之前調用過?alarm()?且鬧鐘還未超時,再次調用會重置鬧鐘,新的?seconds?值會覆蓋舊值。

  • 返回值: 返回上一次設置的鬧鐘的剩余秒數,如果之前沒有鬧鐘則返回0。

示例

#include <stdio.h>
#include <unistd.h>int main() {printf("First alarm set for 5 seconds.\n");unsigned int ret = alarm(5); // ret = 0sleep(2); // Sleep for 2 secondsprintf("Resetting alarm for 3 seconds from now.\n");ret = alarm(3); // ret = 5 - 2 = 3 (seconds left from previous alarm)printf("Previous alarm had %u seconds left.\n", ret);sleep(10); // Sleep longer than the alarmprintf("This line will not be printed because SIGALRM terminated the process.\n");return 0;
}
3.3.4?signal()?- 信號處理
#include <signal.h>typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
  • 功能: 修改進程對特定信號?signum?的處理方式。

  • 參數

    • signum: 要捕獲的信號編號。

    • handler

      • SIG_IGN: 忽略此信號。

      • SIG_DFL: 恢復對此信號的默認處理。

      • 函數指針: 程序員自定義的信號處理函數地址。該函數必須具有?void func(int sig_num)?的格式。

  • 返回值: 成功時返回上一次的信號處理函數指針,失敗返回?SIG_ERR

捕獲處理示例

#include <stdio.h>
#include <signal.h>
#include <unistd.h>// 自定義信號處理函數
void my_handler(int sig_num) {printf("\nCaught signal %d! I'm not going to die!\n", sig_num);// 注意:在信號處理函數中使用printf等標準IO函數可能是不安全的,這里僅作演示
}int main() {// 捕獲SIGINT信號 (Ctrl+C)if (signal(SIGINT, my_handler) == SIG_ERR) {perror("Signal setup failed");return 1;}printf("Process PID: %d. Try pressing Ctrl+C...\n", getpid());while(1) {pause(); // 無限期休眠,等待任何信號到來}return 0;
}

3.4 重要補充知識

3.4.1?waitpid()?與進程退出狀態

waitpid?不僅可以等待子進程結束,還能獲取其詳細的退出信息。

#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *wstatus, int options);
wstatus?是一個輸出參數,由內核填充狀態信息。需要使用一系列宏來解析:WIFEXITED(wstatus): 如果子進程正常終止(通過?exit?或?return),則返回真。WEXITSTATUS(wstatus): 如果?WIFEXITED?為真,此宏提取子進程的退出碼(exit?的參數)。WIFSIGNALED(wstatus): 如果子進程是被信號殺死的,則返回真。WTERMSIG(wstatus): 如果?WIFSIGNALED?為真,此宏提取導致子進程終止的信號編號。WIFSTOPPED(wstatus)?/?WSTOPSIG(wstatus): 用于檢查暫停的信號。

示例

pid_t pid = fork();
if (pid == 0) {// Child process// ... maybe do something that causes a segfaultexit(10);
} else {int wstatus;waitpid(pid, &wstatus, 0);if (WIFEXITED(wstatus)) {printf("Child exited normally with code: %d\n", WEXITSTATUS(wstatus));} else if (WIFSIGNALED(wstatus)) {printf("Child was killed by signal: %d\n", WTERMSIG(wstatus));}
}
3.4.2?atexit()?- 注冊退出清理函數
#include <stdlib.h>
int atexit(void (*function)(void));
  • 功能: 注冊一個函數,當進程通過?exit()?函數正常退出時,該注冊函數會被自動調用。

  • 特點: 可以注冊多個函數,它們的執行順序與注冊順序相反(LIFO,后進先出)。

  • 注意: 如果進程是被信號殺死的,這些函數不會被執行。

示例

#include <stdio.h>
#include <stdlib.h>void cleanup1() { printf("Performing cleanup 1...\n"); }
void cleanup2() { printf("Performing cleanup 2...\n"); }int main() {atexit(cleanup1);atexit(cleanup2); // This will be called firstprintf("Main function is running...\n");// exit(0); // atexit functions will be called// If we use _exit(0) or are killed by a signal, cleanup won't happen.return 0; // return calls exit implicitly
}
// Output:
// Main function is running...
// Performing cleanup 2...
// Performing cleanup 1...

4. 實戰任務:音樂播放器控制器

現在,我們綜合運用?fork,?exec,?waitpid,?signal?等知識,實現一個簡單的后臺音樂播放器控制器。

4.1 需求分析

父進程作為控制器,負責:

  1. 顯示菜單:1:上一首 2:下一首 3:暫停 4:繼續 0:退出

  2. 接收用戶輸入,根據輸入向子進程(播放器)發送不同的控制信號。

  3. 優雅地處理子進程的退出。

子進程負責:

  1. 使用?execlp?調用?mpg123?程序來播放音樂。

  2. 根據父進程發來的信號做出反應(播放、暫停、切歌)。

4.2 核心設計思路與流程圖

父進程通過?fork?+?exec?創建子進程來播放音樂。父進程通過信號 (SIGINT,?SIGSTOP,?SIGCONT?等) 來控制子進程的狀態(暫停、繼續、終止)。同時,父進程需要捕獲?SIGCHLD?信號,以便在子進程意外結束時(比如一首歌放完了)能及時知曉并可能播放下一首。

圖表

代碼

4.3 代碼實現框架

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <glob.h> // For finding music filespid_t player_pid = -1;
int current_song_index = 0;
int song_count = 0;
char **song_list = NULL;// 自定義SIGCHLD處理函數
void child_handler(int sig) {int wstatus;pid_t pid;// 非阻塞地等待所有結束的子進程while ((pid = waitpid(-1, &wstatus, WNOHANG)) > 0) {if (pid == player_pid) {printf("Music player process (PID: %d) ended.\n", player_pid);player_pid = -1;// 如果不是父進程主動殺的(比如歌曲放完了),則播下一首if (WIFEXITED(wstatus) || WIFSIGNALED(wstatus)) {// 簡單策略:一首歌放完就播下一首current_song_index = (current_song_index + 1) % song_count;printf("Moving to next song: %d\n", current_song_index);}}}
}// 退出清理函數
void cleanup() {system("stty echo");    // 恢復終端回顯printf("\033[?25h");    // 顯示光標if (player_pid > 0) {kill(player_pid, SIGKILL); // 確保子進程被殺死}// 釋放song_list內存...
}// 啟動播放器子進程
void start_player() {if (player_pid > 0) {kill(player_pid, SIGINT); // 先殺死之前的播放進程// wait for it to die... (handled by SIGCHLD)sleep(1);}player_pid = fork();if (player_pid == 0) {// Child process: become the music playerexeclp("mpg123", "mpg123", "-q", song_list[current_song_index], NULL);perror("execlp failed");exit(1);} else if (player_pid < 0) {perror("fork failed");}
}int main() {// 1. 查找音樂文件 (e.g., *.mp3)glob_t glob_result;glob("*.mp3", GLOB_TILDE, NULL, &glob_result);song_count = glob_result.gl_pathc;song_list = glob_result.gl_pathv;if (song_count == 0) {printf("No MP3 files found!\n");exit(1);}// 2. 設置信號處理和清理函數signal(SIGCHLD, child_handler);atexit(cleanup);// 3. 啟動第一首歌start_player();// 4. 主控制循環int choice;while (1) {printf("\n1:Prev | 2:Next | 3:Pause | 4:Resume | 0:Exit\n");scanf("%d", &choice);switch (choice) {case 0: // Exitif (player_pid > 0) {kill(player_pid, SIGKILL);}return 0;case 1: // Previouscurrent_song_index = (current_song_index - 1 + song_count) % song_count;start_player();break;case 2: // Nextcurrent_song_index = (current_song_index + 1) % song_count;start_player();break;case 3: // Pauseif (player_pid > 0) kill(player_pid, SIGSTOP);break;case 4: // Resumeif (player_pid > 0) kill(player_pid, SIGCONT);break;default:printf("Invalid choice.\n");}}return 0;
}

編譯與運行

gcc music_player.c -o music_player
./music_player

(確保系統已安裝?mpg123sudo apt-get install mpg123)

5. 注意事項

5.1 信號處理的安全問題

信號處理函數是在異步環境中執行的,這意味著它可能在主程序執行的任何點被調用。因此,在信號處理函數中調用諸如?printfmalloc?等非異步信號安全(async-signal-safe)的函數是不安全的。POSIX.1 標準定義了一個異步信號安全的函數列表,詳見?man 7 signal-safety。在信號處理函數中,應盡量只做簡單的標志設置,或者使用?write?函數向標準輸出寫入簡單消息。

5.2 更現代的信號處理接口:sigaction

雖然?signal()?函數簡單易用,但它在不同Unix版本中的行為可能略有差異(可移植性問題)。更現代、更強大的替代者是?sigaction()?函數,它提供了對信號處理更精確的控制,例如:

  • 指定在處理信號時是否自動阻塞其他信號。

  • 獲取信號被觸發時的各種上下文信息。

  • 避免信號處理函數執行后被重置為默認行為(某些系統下signal()會有此問題)。

建議在新代碼中使用?sigaction

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/93811.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/93811.shtml
英文地址,請注明出處:http://en.pswp.cn/web/93811.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

LoRa 網關組網方案(二)

LoRa 網關組網方案 現有需求&#xff1a;網關每6秒接收不同節點的數據&#xff0c;使用SX1262芯片。 以下是完整的組網方案&#xff1a;1. 網絡架構設計 采用星型拓撲&#xff1a; 網關&#xff1a;作為中心節點&#xff0c;持續監聽多個信道節點&#xff1a;分布在網關周圍&am…

服裝外貿系統軟件怎么用才高效防風險?

服裝外貿系統軟件概述 服裝外貿系統軟件&#xff0c;如“艾格文ERP”&#xff0c;是現代外貿企業不可或缺的管理工具。它整合了訂單處理、庫存管理、客戶資源保護、財務控制等多功能模塊&#xff0c;旨在全面提升業務運營效率。通過系統化的管理方式&#xff0c;艾格文ERP能夠從…

【沉浸式解決問題】peewee.ImproperlyConfigured: MySQL driver not installed!

目錄一、問題描述二、原因分析三、解決方案? 推薦&#xff1a;安裝 pymysql&#xff08;純 Python&#xff0c;跨平臺&#xff0c;安裝簡單&#xff09;? 可選&#xff1a;安裝 mysqlclient&#xff08;更快&#xff0c;但需要本地編譯環境&#xff09;? 總結四、mysql-conn…

C++進階-----C++11

作者前言 &#x1f382; ??????&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ?&#x1f382; 作者介紹&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

(論文速讀)航空軸承剩余壽命預測:多生成器GAN與CBAM融合的創新方法

論文題目&#xff1a;Remaining Useful Life Prediction Approach for Aviation Bearings Based on Multigenerator Generative Adversarial Network and CBAM&#xff08;基于多發生器生成對抗網絡和CBAM的航空軸承剩余使用壽命預測方法&#xff09;期刊&#xff1a;IEEE TRAN…

3ds Max 流體模擬終極指南:從創建到渲染,打造真實液體效果

流體模擬是提升 3D 場景真實感的重要技術之一。無論是模擬飛瀑流泉、杯中溢出的飲料&#xff0c;還是黏稠的蜂蜜或熔巖&#xff0c;熟練掌握流體動力學無疑能為你的作品增色不少。本文將以 3ds Max 為例&#xff0c;系統講解流體模擬的創建流程與渲染方法&#xff0c;幫助你實現…

《算法導論》第 35 章-近似算法

大家好&#xff01;今天我們深入拆解《算法導論》第 35 章 ——近似算法。對于 NP 難問題&#xff08;如旅行商、集合覆蓋&#xff09;&#xff0c;精確算法在大規模數據下往往 “力不從心”&#xff0c;而近似算法能在多項式時間內給出 “足夠好” 的解&#xff08;有嚴格的近…

系統架構設計師-操作系統-避免死鎖最小資源數原理模擬題

寫在前面&#xff1a;銀行家算法的核心目標是確保系統始終處于“安全狀態”。一、5個進程各需2個資源&#xff0c;至少多少資源避免死鎖&#xff1f; 解題思路 根據死鎖避免的資源分配公式&#xff0c;不發生死鎖的最少資源數為&#xff1a; 最少資源數k(n?1)1 \text{最少資源…

Preprocessing Model in MPC 2 - 背景、基礎原語和Beaver三元組

參考論文&#xff1a;SoK: Multiparty Computation in the Preprocessing Model MPC (Secure Multi-Party Computation) 博士生入門資料。抄襲必究。 本系列教程將逐字解讀參考論文(以下簡稱MPCiPPM)&#xff0c;在此過程中&#xff0c;將論文中涵蓋的40篇參考文獻進行梳理與講…

ACCESS/SQL SERVER保存軟件版本號為整數類型,轉成字符串

在 Access 中&#xff0c;若已將版本號&#xff08;如1.3.15&#xff09;轉換為整數形式&#xff08;如10315&#xff0c;即1*10000 3*100 15&#xff09;&#xff0c;可以通過 SQL 的數學運算反向解析出原始版本號格式&#xff08;主版本.次版本.修訂號&#xff09;。實現思…

編程語言學習

精通 Java、Scala、Python、Go、Rust、JavaScript ? 1. Java 面向對象編程&#xff08;OOP&#xff09;、異常處理、泛型JVM 原理、內存模型&#xff08;JMM&#xff09;、垃圾回收&#xff08;GC&#xff09;多線程與并發&#xff08;java.util.concurrent&#xff09;Java 8…

軟件測試:如何利用Burp Suite進行高效WEB安全測試

Burp Suite 被廣泛視為 Web 應用安全測試領域的行業標準工具集。要發揮其最大效能&#xff0c;遠非簡單啟動掃描即可&#xff0c;而是依賴于測試者對其模塊化功能的深入理解、有機組合及策略性運用。一次高效的測試流程&#xff0c;始于精細的環境配置與清晰的測試邏輯。測試初…

華為認證 HCIA/HCIP/HCIE 全面解析(2025 版)

說實話&#xff0c;想在IT行業站穩腳跟&#xff0c;沒有過硬的技術和資歷&#xff0c;光憑熱情和一腔干勁根本不行。 而華為認證&#xff0c;作為業內公認的“技術護照”&#xff0c;已經成了許多人打開職場大門的關鍵。 你會發現&#xff0c;越來越多的企業在招聘時&#xff0…

ComfyUI-3D-Pack:3D創作的AI神器

一、應用介紹 單圖轉3D網格&#xff1a;輸入一張角色圖&#xff0c;能輸出基本成型的3D Mesh&#xff0c;還自帶UV展開和貼圖輸出&#xff0c;可直接導入到Blender等軟件中使用。多視角圖像生成&#xff1a;可以基于算法生成圍繞3D模型的多視角圖像&#xff0c;用于3D模型展示…

【java面試day15】mysql-聚簇索引

文章目錄問題&#x1f4ac; Question 1&#x1f4ac; Question 2相關知識問題 &#x1f4ac; Question 1 Q&#xff1a;什么是聚簇索引&#xff0c;什么是非聚簇索引&#xff1f; A&#xff1a;聚簇索引主要是指數據與索引放到一塊&#xff0c;B樹的葉子節點保存了整行數據&a…

【typenum】 16 無符號整數標記

一、源碼 這段代碼是 Rust 中用于實現編譯時無符號整數的核心部分。它定義了一個 Unsigned trait 并為兩種類型實現了該 trait&#xff1a;UTerm&#xff08;表示零&#xff09;和 UInt<U, B>&#xff08;表示非零數字&#xff09;。 定義&#xff08;marker_traits.rs&a…

重溫k8s基礎概念知識系列四(服務、負載均衡和聯網)

文章目錄1、Kubernetes 網絡模型2、為什么需要 Service&#xff1f;2.1、定義service2.2、Service的類型2.3、Service 工作原理2.4、Service 與 DNS3、Ingress&#xff08;高級流量管理&#xff09;3.1、定義Ingress 資源3.2、Ingress 規則4、常見面試高頻問答5、總結1、Kubern…

基于SpringBoot的停車場管理系統【2026最新】

作者&#xff1a;計算機學姐 開發技術&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源碼”。 專欄推薦&#xff1a;前后端分離項目源碼、SpringBoot項目源碼、Vue項目源碼、SSM項目源碼、微信小程序源碼 精品專欄&#xff1a;…

Nginx前后端分離反代(VUE+FastAPI)

原文鏈接&#xff1a;Nginx前后端分離反代&#xff08;VUEFastAPI&#xff09; < Ping通途說 0.前言 工作需求自己全棧開發了一個后臺后端&#xff0c;要求前后端分離&#xff0c;即nginx靜態代理前端文件&#xff0c;再代理后端接口。以前自己也遇過這種情況&#xff0c;但…

豆包1.5 Vision Lite 對比 GPT-5-min,誰更適合你?實測AI模型選型利器 | AIBase

“團隊要上線一個智能客服系統&#xff0c;預算有限&#xff0c;中文場景為主&#xff0c;偶爾需要讀圖——該選豆包1.5還是GPT-5-min&#xff1f;” “個人開發者想接大模型API做寫作助手&#xff0c;要求響應快、成本低&#xff0c;Claude Haiku、Moonshot、GPT-5-min 哪個更…