Linux信號三部曲:產生機制、處理方式與內核接口

Linux系列


文章目錄

  • Linux系列
  • 前言
  • 一、背景知識鋪墊
    • 1.1 信號的基本概念
    • 1.2 進程對信號的處理
  • 二、信號的產生
    • 2.1 前臺進程和后臺進程
    • 2.2 鍵盤組合鍵
    • 2.3 kill 命令
    • 2.4 系統調用
      • 2.4.1 signal()接口
      • 2.4.2 kill()接口
      • 2.4.3 raise()接口
      • 2.4.4 abort()接口
  • 總結


前言

Linux中,信號(Signal)是一種進程間通信(IPC)的機制,它用來通知進程發生了特定的事件。進程接受到信號后會根據信號的類型結合自己的處理方式做出相應的處理。


一、背景知識鋪墊

1.1 信號的基本概念

Linux信號是一種異步通信機制,用于在進程之間傳遞事件或在系統于進程之間進行交互。當發生某個特定事件時,如:用戶輸入特定組合建(Ctrl+c等)、進程異常終止,系統就會向相關進程發送信號。

1.2 進程對信號的處理

進程在被設計時,就內置了識別信號的方法以及默認處理不同信號方式,當進程接收到信號時,并不一定會立即處理,這也就要求進程需要具有保存信號的能力,當等到合適的時候,進程會根據信號的類型結合自己的處理方式法,做出處理。
進程在處理信號的方式:

  • 默認處理方式(進程內置的)
  • 忽略信號
  • 自定義處理方式(捕捉信號后,使用用戶設定的方法)

二、信號的產生

穿插了一部分拓展知識

2.1 前臺進程和后臺進程

首先我們先來看下面這個程序:

#include<iostream>
#include<unistd.h>
using namespace std;int main()
{while(true){cout<<"I'm a crazy process,PID:"<<getpid()<<endl;sleep(1);}return 0;
}

在這里插入圖片描述
當我們執行該程序后,再輸入ls、pwd,可以看到指令并不會執行,進程則一直運行,當我們使用Ctrl+c就可以將進程終止,這樣的進程就是前臺進程。再次執行該程序:

在這里插入圖片描述
這次我們以后臺進程的形式執行該進程./可執行程序 &,可以發現,當進程執行后,我們再輸入指令,此時指令是可以成功執行的,當我們使用Ctrl+c時無法終止進程。這種進程為后臺進程。

Linux中,一次登錄,一個終端,一般配有一個bash,而每個終端只允許有一個前臺進程,可有多個后臺進程,當我們執行./process時,前臺進程就由bash變為了./process而鍵盤輸入是優先被前臺進程獲取的,所以指令無法被執行,但前臺進程./peocess接收到Ctrl+c信號時就會終止。這樣我們再來理解第二個現象,當我們以后臺進程運行./process是,此時bash仍被視為前臺進程,當用戶輸入指令是仍可被接收并執行,此時再輸入Ctrl+c信號./process進程并沒有接收,所以沒有終止。

前臺進程:會獨占終端,直到進程執行完成或者被掛起,在這期間終端無法接受其他命令輸入,用戶只能與該進程進行交互。
后臺進程:不會占用終端,終端可以繼續接受用戶輸入的其他命令,用戶可以在同一個終端中同時啟動多個后臺進程。

前臺進程:其執行過程會受到用戶操作的直接影響,比如用戶可以通過鍵盤輸入來中斷或暫停進程。如果終端關閉,前臺進程通常會被終止,除非進行了特殊的設置。
后臺進程:通常是長時間運行的,不受終端關閉的影響,除非明確地對其進行停止或重啟操作。它按照自身的邏輯和任務需求在后臺持續運行,不會因為用戶的一些常規操作而中斷

Ctrl+c本質會解釋為2號信號,后面我們會驗證

2.2 鍵盤組合鍵

這是我們在學習Linux過程中,比較常用的一種向進程發送信號的方式,它通過一些特定的鍵盤組合鍵,來發送一些特殊的信號,如Ctrl+c終止進程。組合有很多種,都比較簡單,這里我們想要介紹的是,Ctrl+c這類組合鍵是如何被轉換為信號,又是如何被進程接收的?

我們可以確定的意見事是,CPU不能直接從鍵盤讀取數據(馮諾依曼體系結構),那么這個工作只能交由操作系統來完成,操作系統又是如何得知鍵盤有數據了呢?我們根據下圖來回答:
在這里插入圖片描述

CPU上有很多針腳,每個針腳對應一個硬件設備(鍵盤、網卡),當用戶按下Ctrl+c組合鍵時,鍵盤發生硬件中斷,產生中斷號,通過對于針腳發送(充放電)給CPU,通知CPU進行相關處理,操作系統從CPU讀取到中斷號,通過中斷號在自己的中斷向量表中索引到對應方法地址,執行該方法(讀取鍵盤),操作系統識別如果是數據直接讀取,如果是組合鍵就將他解釋成對應信號,如ctrl+c解釋為2號信號,并將它讀入鍵盤緩沖區(一切皆文件),再拷貝至用戶緩沖區,被進程接收,進程執行對應處理方法。我們的信號處理方式(異步通知、事件驅動等)就是模擬的硬件中斷,因此信號又被叫做軟件中斷。

2.3 kill 命令

對于上面的后臺進程,我們可以通過kill指令的形式,給它發送信號,終止它,可以通過kill -l查看信號種類:

在這里插入圖片描述
0~31為普通信號,34~64為實時信號(我們不研究),這里有多種信號來都能達到終止,后臺進程的要求,如:2號終止進程,9號殺掉進程。
使用格式:kill -信號編號 進程PID.

常用信號

信號編號信號名稱觸發方式作用
2SIGINTCtrl+C終止前臺進程
3SIGQUITCtrl+\終止進程并生成core文件
9SIGKILLkill -9 pid強制終止進程,不可被捕獲或忽略
15SIGTERMkill -15 pid正常終止進程,進程可捕獲并進行清理
1SIGHUP終端連接斷開等讓進程重新初始化或終止
18SIGCONTkill -18 pid繼續執行暫停的進程
19SIGSTOPkill -19 pid暫停進程,不可被捕獲或忽略
20SIGTSTPCtrl+Z暫停前臺進程,將其放入后臺
10SIGUSR1kill -10 pid用戶自定義信號,用于特定程序邏輯
12SIGUSR2kill -12 pid用戶自定義信號,用于特定程序邏輯

2.4 系統調用

2.4.1 signal()接口

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

功能:捕捉指定信號,執行自定義功能

參數

  1. signum: 要捕捉信號編號
  2. handler:函數指針,用互自定義的方法

下面的程序我們使用signal函數捕捉二號信號,執行我們自定義的方法。

#include<iostream>
#include<unistd.h>
#include<signal.h>
using namespace std;void handler(int num)
{cout<<"I captured Signal No."<<num<<endl;return ;
}
int main()
{sighandler_t _handler=signal(2,handler);while(true){cout<<"I'm a crazy process,PID:"<<getpid()<<endl;sleep(1);}return 0;
}

在這里插入圖片描述
可以看到此時我們再使用Ctrl+c,信號程序就不會終止,而是執行我們的自定義方法。上面的場景也完美的呈現了,信號的異步性(程序先調用singnal,但是此時進程沒有收到信號,所以這個函數不會執行,當進程接收到信號后,就會執行對于方法)。
需要注意的是,并不是所有信號,都可以被捕捉的,我們可以通過下面的方式來驗證:

#include<iostream>
#include<unistd.h>
#include<signal.h>
using namespace std;void handler(int num)
{cout<<"I captured Signal No."<<num<<endl;return ;
}
int main()
{for(int i=0;i<=64;i++)sighandler_t _handler=signal(i,handler);while(true){cout<<"I'm a crazy process,PID:"<<getpid()<<endl;sleep(1);}return 0;
}

執行這個進程時,通過kill指令向進程發信號,這里不方便演示,大家可以自己嘗試(9號和19號好像不能被捕捉)。

2.4.2 kill()接口

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

功能:向指定進程發送信號

參數

  1. pid:接收信號的進程PID
  2. sig:信號編號

返回值
執行成功返回零,失敗飯回-1并設置errno

示例:

#include<iostream>
#include<signal.h>
#include<sys/types.h>
using namespace std;
int main(int argc,char*argv[])
{if(argc!=3){cout << "Usage:\n\t" << argv[0] << " signum pid\n\n";exit(1);}int signum=stoi(argv[1]);int pid=stoi(argv[2]);int n=kill(pid,signum);if(n==-1){perror("kill");exit(1);}return 0;
}

現在我們就可以使用上面這個進程,來對其他進程發送信號了。
在這里插入圖片描述
當然你可以讓你的進程直接使用kill自己給自己發信號。

2.4.3 raise()接口

#include <signal.h>
int raise(int sig);

功能:讓當前進程給自己發送信號

參數

  • sig:信號編號
#include<iostream>
#include<unistd.h>
#include<signal.h>
using namespace std;void handler(int num)
{cout<<"I captured Signal No."<<num<<endl;return ;
}
int main()
{sighandler_t _handler=signal(2,handler);int cnt=5;while(true){cout<<"I'm a crazy process,PID:"<<getpid()<<endl;cnt--;if(cnt==0)break;//跳出循環sleep(1);}raise(9);cout<<"111111"<<endl;return 0;

在這里插入圖片描述
循環執行五次后跳出,執行raise()完成“自殺”,程序終止,我們可以感受到raise()其實就是分裝的kill()

2.4.4 abort()接口

#include <stdlib.h>
void abort(void);

功能:向當前進程發送SIGABRT(6號)信號,默認情況下,進程收到該信號后會立即終止,即使被用戶捕獲,在執行過用戶提供的方法后依然終止進程。

#include<iostream>
#include<unistd.h>
#include<signal.h>
using namespace std;void handler(int num)
{cout<<"I captured Signal No."<<num<<endl;return ;
}
int main()
{sighandler_t _handler=signal(6,handler);while(true){cout<<"I'm a crazy process,PID:"<<getpid()<<endl;abort();}return 0;
}

在這里插入圖片描述

如果不調用該函數,使用指令發送六號信號,當信號被捕獲后,進程不會終止,abrot函數內部做了特殊處理,才會使進程終止,你可以測試看看。

總結

特點:
異步傳遞(隨時可能中斷進程)

  • SIGINT(2):由鍵盤輸入Ctrl+C產生,用于中斷正在運行的進程。
  • SIGKILL(9):強制終止進程,不能被捕獲和忽略,用于緊急情況下終止進程。
  • SIGSTOP(19):暫停進程,不能被捕獲和忽略,可使用SIGCONT信號恢復進程運行

不可捕獲的信號

SIGKILL(9)和SIGSTOP(19)無法被捕獲、阻塞或忽略,用于強制控制進程。
信號發送函數

  • kill函數:可以向指定進程發送指定信號,例如 kill(pid, SIGINT) 向進程號為 pid 的進程發送 SIGINT 信號。
  • raise函數:用于向當前進程發送信號,如 raise(SIGABRT) 向當前進程發送 SIGABRT 信號。

處理方式:

  • 默認處理:系統為每個信號定義了默認的處理行為,如終止進程、產生核心轉儲、忽略信號等。
  • 捕獲信號:進程可以通過 signal 函數當接收到指定信號時,執行自定義的處理邏輯。
  • 忽略信號:進程可以使用 signal 函數將信號設置為忽略,使進程不響應該信號,但有些信號(如 SIGKILL 和 SIGSTOP )不能被忽略(這點我在下篇介紹)。

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

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

相關文章

win7/win10/macos如何切換DNS,提升網絡穩定性

本篇教程教您如何在Windows10、Windows8.1、Windows7、MacOS操作系統切換DNS&#xff0c;以提升系統的穩定性&#xff0c;獲得更好的操作體驗。 Windows10及Windows8.1 1、右鍵單擊“此計算機”&#xff0c;然后選擇“屬性”。進入Windows系統界面后&#xff0c;選擇左側的“…

移動硬盤突然打不開緊急救援指南:從排查到完整恢復?

突發狀況的典型特征? 當移動硬盤突然打不開時&#xff0c;用戶常會遇到多種異常表現&#xff1a;接入電腦后硬盤指示燈雖亮但無法識別、系統反復提示“設備未連接成功”或彈出“磁盤結構損壞”的警告。部分情況下&#xff0c;資源管理器中的盤符雖可見&#xff0c;但雙擊后顯示…

華為OD機試真題——統計匹配的二元組個數(2025A卷:100分)Java/python/JavaScript/C++/C語言/GO六種最佳實現

2025 A卷 100分 題型 本文涵蓋詳細的問題分析、解題思路、代碼實現、代碼詳解、測試用例以及綜合分析&#xff1b; 并提供Java、python、JavaScript、C、C語言、GO六種語言的最佳實現方式&#xff01; 2025華為OD真題目錄全流程解析/備考攻略/經驗分享 華為OD機試真題《統計匹配…

半導體制造如何數字化轉型

半導體制造的數字化轉型正通過技術融合與流程重構&#xff0c;推動著這個精密產業的全面革新。全球芯片短缺與工藝復雜度指數級增長的雙重壓力下&#xff0c;頭部企業已構建起四大轉型支柱&#xff1a; 1. 數據中樞重構產線生態 臺積電的「智慧工廠4.0」部署著30萬物聯網傳感器…

[Spark]深入解密Spark SQL源碼:Catalyst框架如何優雅地解析你的SQL

本文內容組織形式 總結具體例子執行語句解析層優化層物理計劃層執行層 猜你喜歡PS 總結 先寫個總結&#xff0c;接下來會分別產出各個部分的源碼解析&#xff0c;Spark SQL主要分為以下五個執行部分。 具體例子 接下來舉個具體的例子來說明 執行語句 SELECT name, age FR…

【數據結構】4.單鏈表實現通訊錄

在上一篇文章我們學會了用單鏈表來實現各種方法&#xff0c;在這一篇文章我們將在單鏈表的基礎上實現通訊錄。 0、準備工作 實現通訊錄之前&#xff0c;我們還需要在單鏈表的基礎上添加2個文件&#xff0c;頭文件Contact.h和源文件Contact.c。Contact.c來實現通訊錄方法的聲明…

【bash】.bashrc

查看當前路徑文件數量 alias file_num"ls -l | grep ^- | wc -l"查看文件大小 alias file_size"du -sh"alias ll alias ll"ls -ltrh"cd的同時執行ll alias cdcdls; function cdls() {builtin cd "$1" && ll }自定義prompt…

微信小程序實戰案例 - 餐館點餐系統 階段 2 – 購物車

階段?2 – 購物車&#xff08;超詳細版&#xff09; 目標 把“加入購物車”做成 全局狀態&#xff0c;任何頁面都能讀寫在本地 持久化&#xff08;關閉小程序后購物車仍在&#xff09;新建 購物車頁&#xff1a;數量增減、總價實時計算、去結算入口打 Git?Tag v2.0?cart 1. …

從紅黑樹到哈希表:原理對比與典型場景應用解析(分布式以及布隆過濾器)

在數據結構的世界里&#xff0c;紅黑樹一直以「自平衡二叉查找樹」的身份備受贊譽。憑借紅黑節點的精妙設計&#xff0c;它能將插入、刪除、查找的時間復雜度穩定控制在 ( log ? n ) (\log n) (logn)&#xff0c;成為處理有序數據的經典方案。然而&#xff0c;當業務場景對「…

游戲報錯?MFC140.dll怎么安裝才能解決問題?提供多種MFC140.dll丟失修復方案

MFC140.dll 是 Microsoft Visual C 2015 運行庫的重要組成部分&#xff0c;許多軟件和游戲依賴它才能正常運行。如果你的電腦提示 "MFC140.dll 丟失" 或 "MFC140.dll 未找到"&#xff0c;說明系統缺少該文件&#xff0c;導致程序無法啟動。本文將詳細介紹 …

《電子類專業:通往科技未來的鑰匙》

一、電子類專業全景概覽 在當今科技飛速發展的時代,電子類專業無疑占據著現代科技體系中基礎與核心的重要地位。從我們日常生活中不可或缺的智能手機、電腦,到推動社會進步的人工智能、大數據技術,再到探索宇宙奧秘的航天航空設備,電子類專業的身影無處不在。它就像一把萬…

Java--批量刪除

前端部分 前端代碼主要負責收集用戶選擇的學生記錄的 id&#xff0c;并將這些 id 發送給后端的 DeleteMoreServlet 進行處理。 批量刪除按鈕綁定點擊事件 $(".deleteMore").on("click",function(){// ... }); 當用戶點擊 “批量刪除” 按鈕時&#xff…

2025年4月份生活有感

今天在5000B培訓的下午&#xff0c;一起入所來的小伙伴&#xff0c;有個申請了深圳大學的博士&#xff0c;已錄取。哎&#xff0c;想起了當年申博時候信心和決心不足&#xff0c;導致后面匆匆的拿了offer去工作。看到同事的選擇還是非常羨慕&#xff0c;想到自己5月份的婚禮&am…

數學建模學習資料免費分享:歷年賽題與優秀論文、算法課程、數學軟件等

本文介紹并分享自己當初準備數學建模比賽時&#xff0c;收集的所有資料&#xff0c;包括歷年賽題與論文、排版模板、算法講解課程與書籍、評分標準、數學建模軟件等各類資料。 最近&#xff0c;準備將自己在學習過程中&#xff0c;到處收集到的各類資料都整理一下&#xff0c;并…

關于 微服務負載均衡 的詳細說明,涵蓋主流框架/解決方案的對比、核心功能、配置示例及總結表格

以下是關于 微服務負載均衡 的詳細說明&#xff0c;涵蓋主流框架/解決方案的對比、核心功能、配置示例及總結表格&#xff1a; 1. 負載均衡的核心概念 負載均衡在微服務中用于將請求分發到多個服務實例&#xff0c;以實現&#xff1a; 高可用性&#xff1a;避免單點故障。性…

個人博客搭建過程

嘗試博客搭建,前面注冊部分很簡單&#xff0c;就不寫了&#xff0c;以我看到的一個博客為基礎&#xff0c;加上我自己的測試結束 1.官網 https://dash.infinityfree.com 前半部分參考&#xff1a; 使用Infinityfree免費虛擬主機搭建一個wordpress網站 2.創建賬戶或登錄您的…

Proxmox VE 網絡配置命令大全

如果對 Proxmox VE 全棧管理感興趣&#xff0c;可以關注“Proxmox VE 全棧管理”專欄&#xff0c;后續文章將圍繞該體系&#xff0c;從多個維度深入展開。 概要&#xff1a;Proxmox VE 網絡配置靈活&#xff0c;滿足虛擬化組網需求。基礎靠橋接實現虛擬機與物理網絡互聯&#x…

【QT入門到晉級】QT打動態庫包及引入動態庫包

前言 本篇為持續更新狀態&#xff0c;內容包含window、Linux下打動態庫包&#xff0c;以及引入動態庫包的方式。 一、window 1、動態庫打包 以百度的OCR接口調用打dll庫為例&#xff0c;以下為qtcreator創建動態庫過程&#xff1a; 1.1Qtcreator創建lib項目 創建成功后&…

Uniapp: 大綱

目錄 一、基礎鞏固1.1、Uniapp:下拉選擇框ba-tree-picker1.2、Uniapp&#xff1a;確認框1.3、Uniapp&#xff1a;消息提示框1.4、Uniapp&#xff1a;列表提示框1.5、Uniapp&#xff1a;獲取當前定位坐標 二、項目配置2.1、Uniapp&#xff1a;修改端口號2.2、Uniapp&#xff1a;…

WPF 從Main()方法啟動

1.去掉App.xaml StartupUri“MainWindow.xaml” 只會讓App.g.cs 不生成這行代碼&#xff0c;但是還是會生成的App.g.cs文件中生成Main方法 this.StartupUri new System.Uri("MainWindow.xaml", System.UriKind.Relative);默認的App.xaml的生成操作是 應用程序定義…