深入了解linux系統—— 信號的捕捉

前言

信號從產生到處理,可以分為信號產生、信號保存、信號捕捉三個階段;了解了信號產生和保存,現在來深入了解信號捕捉。

信號捕捉

對于1-31號普通信號,進程可以立即處理,也可以不立即處理而是在合適的時候處理;

在合適的時候處理信號,什么時候合適呢?

信號捕捉的流程

要了解信號捕捉的流程,先要了解內核態和用戶態;

簡單來說,內核態就是以操作系統的身份去運行;而用戶態就是以用戶的身份去運行。(后面再詳細說明)

這里直接來看信號捕捉的流程:

我們的進程在正常執行,在執行到某條指令,因為系統調用、中斷或異常從而進入內核;

而內核處理完異常之后,準備回到用戶之前,就會處理當前進程可以遞達的信號;

處理信號,執行so_signal方法,如果進程對于信號是自定義捕捉,處理信號就要從內核態回到用戶態處理信號;

自定義捕捉完信號之后,就要再回到內核態,然后由內核態再回到用戶態,從上次被中斷的地方繼續向下執行。

以自定義捕捉為例,信號捕捉的流程如下圖所示:

在這里插入圖片描述

所以,在信號捕捉的整個流程中,存在4次用戶態和內核態的轉換;簡化成以下圖:

在這里插入圖片描述

簡單總結描述信號捕捉流程:

  1. 用戶進程執行
    • 進程在用戶空間正常執行代碼
  2. 進入內核
    • 發生系統調用/中斷/異常 → CPU自動切換到內核態
  3. 內核處理事件
    • 內核完成系統調用/中斷/異常的處理
  4. 信號檢查
    • 內核返回用戶態前檢查信號:
      有未處理且未阻塞的信號? → 繼續
      無信號 → 直接返回用戶態
  5. 準備信號處理(針對自定義信號)
    • 內核在用戶棧創建"信號棧幀"(包含):
      • 信號處理函數地址
      • 原始執行狀態(寄存器值)
      • rt_sigreturn系統調用地址
  6. 第一次返回用戶態
    • 內核修改CPU狀態:
      • 指令指針 → 信號處理函數
      • 棧指針 → 新信號棧幀
    • 切換到用戶態執行信號處理函數
  7. 信號處理完成
    • 信號處理函數執行結束(return語句)
    • 自動跳轉到rt_sigreturn系統調用
  8. 第二次進入內核
    • 執行rt_sigreturn系統調用 → 進入內核態
    • 內核從信號棧幀恢復原始狀態
  9. 最終返回用戶態
    • 內核切換回用戶態
    • 進程從當初被中斷的位置繼續執行

操作系統運行

要了解操作系統是如何運行的,就要先了解一些硬件相關知識

硬件中斷

硬件中斷是外部硬件設備(如鍵盤、鼠標、硬盤、網卡、定時器芯片等)向 CPU 發出的一種緊急通知信號,意思是“我有重要的事情需要你馬上處理!

就像OS是如何知道鍵盤上有數據那樣,并不是OS定期去排查,而是鍵盤給CPU發送中斷,從而讓CPU執行OS中對應的方法。

在這里插入圖片描述

如上圖所示,存在一個中斷控制器,其中每一個中斷號都對應一個外部設備;

  • 當外部設備就緒時,就會向中斷控制器發送中斷,中斷控制器就會通知CPU存在中斷;(向CPU對應針腳發送高低電頻)
  • CPU就會獲取中斷號,然后中斷當前工作并保護現場(保存臨時數據等);
  • OS中存在中斷向量表,其中存儲了對于每一個中斷號的對應處理方法;
  • CPU就會根據中斷號,去執行中斷向量表這對應的中斷處理方法。

中斷向量表是操作系統的一部分,在啟動時就會加載到內存;

通過外部硬件中斷,操作系統就不需要對外設進行周期性檢測;而是當外部設備觸發中斷時,CPU就會執行對應的中斷處理方法。

這種由外部設備觸發,中斷系統運行流程,稱為硬件中斷

時鐘中斷

有了硬件中斷,操作系統就無序去對外設進程周期性檢測;

而操作系統不光要管理硬件資源,也要進行進程調度;那能否按照硬件中斷的原理,定期的向CPU發送中斷,從而定期的執行操作系統的進程調度方法。

在這里插入圖片描述

所以,就有了時鐘源(當代已經集成在CPU內部);就會定期的向CPU發送中斷,CPU通過中斷號去執行中斷向量表中對應的進程調度方法。

那這樣,定期的向CPU發送中斷,也就是定期執行進程調度方法;那進程的時間片,本質上就是一個計數器了,每次調度進程就讓進程的時間片計數器--,當減到0時就說明進程時間片用完,就指定進程調度算法,執行下一個進程。

CPU存在主頻,主頻指的就是時鐘源向CPU發送中斷的頻率,主頻越快,CPU單位時間內就能夠完成更多的操作;CPU就越快。

死循環

有了硬件中斷和時鐘中斷,那操作系統只需要將對應功能添加到中斷向量表中,那操作系統還需要干什么呢?

操作系統的本質:就是死循環

void main()
{//......for(;;)pause();
}

通過查看內核,我們也能夠發現,操作系統在做完內存管理等任務之后,就是死循環。

軟中斷

上述硬件中斷、時鐘中斷都是由硬件觸發的中斷;除此之外呢,也可能因為軟件原因觸發上述中斷。

為了讓操作系統支持進行系統調用,CPU中也設計了匯編指令int(或者syscall),讓CPU內部觸發中斷邏輯。

在這里就要了解一下系統調用了,在之前的認知中,系統調用是由操作系統通過的,我們是直接調用系統調用;

但是,在操作系統中,所有的系統調用都存儲在一張系統調用表當中;(這張系統調用表用于系統調用中中斷處理程序)

我們所調用的系統調用openwrite等等,都是由glibc封裝的;

而想要讓CPU執行對應的方法,就要讓CPU直到對應的系統調用號;

CPU根據系統調用號,然后查表才能調用對應的方法。

在這里插入圖片描述

在這里插入圖片描述

通過觀察,我們也可以發現在glibc的封裝實現,是先將系統調用號寫入寄存器eax;然后再syscall觸發軟中斷,讓CPU根據eax寄存器中的系統調用號執行對應的方法。

內核態和用戶態

在信號捕捉流程中,存在一個概念就是:內核態和用戶態;

我們知道在進程運行時,通過系統調用或者中斷等等陷入內核,進入內核態;而在進行自定義處理時,再有內核態回到用戶態;自定義處理完成之后,再通過特定的系統掉用再進入內核態;最后才回到最初中斷的位置,由內核態進入用戶態。

那內核態和用戶態是什么呢?

簡單來說,內核態就是以操作系統的身份執行;用戶態就是以用戶的身份執行。

在虛擬地址空間(進程地址空間中),[0,3]GB是用戶空間,我們程序的代碼數據、動態庫等等都在用戶這3GB中;而[3,4]GB是內核空間;

在我們的程序中,我們可以返回自己實現的方法、可以調用庫函數;這都是在[0,3]GB用戶空間內進行跳轉的。
執行對應的代碼時,使用用虛擬地址通過頁表(用戶頁表)映射物理地址處,就可以找到對應的代碼和數據。

而在我們調用系統調用時,在進程地址空間中,就要從[0,3]GB用戶空間跳轉到[3,4]GB的內核空間;這樣在執行時,通過內核頁表映射,找到對應內核的代碼運行。

當然,在內核中存在許多進程,這些進程都可能會調用系統調用;而在每一個進程的進程地址空間中的[3,4]GB都是內核空間,都可以通過頁表(內核頁表)映射,找到內存中操作系統的代碼。

所以,我們在進行系統調用時,不用去擔心進程能否在內存中找到對應的地址,因為在進程[3,4]GB內核空間中,有了虛擬地址,通過內核頁表映射,就能夠在內存找到對應的物理地址。

所以,系統調用的執行就是在進程地址空間中進行的。

說了這么多,簡單總結就是:

  • 用戶態就是,在進程地址空間中,通過[0,3]GB用戶空間的虛擬地址,進行頁表映射,執行用戶自己的代碼
  • 內核態就是,通過[3,4]GB內核空間的虛擬地址,進行頁表映射,執行操作系統的代碼

問題:如何知道虛擬地址是[0,3]GB用戶空間的地址還是[3,4]GB內核空間的地址?(CPU執行時如何知道是用戶態還是內核態)

在頁表當中,記錄的不僅僅是虛擬地址和物理地址的映射關系,還用權限(rw)以及當前身份。

此外,在硬件上也存在對應標志:CPU中的Cs段寄存器對應標志位:00(二進制)代表內核、11(二進制)代表用戶。

可重入函數

可重入函數是指可以被多個執行流(例如線程、中斷處理程序、信號處理程序)同時調用,而不會產生錯誤或意外結果的函數

簡單來說就是:

一個可重入函數在執行過程中,如果被另一個執行流打斷并再次進入該函數,當恢復執行時,它仍然能夠正確完成其任務,不會破壞自身的數據或全局狀態。

在這里插入圖片描述

如上圖所示,在調用insert時,執行至某位置,進程收到信號轉而去執行handler方法,而在handler方法中有調用了insert方法;這樣導致了最終的結果不符合我們的預期。

對于一個可重入函數,該函數要滿足:

  1. 不使用靜態(全局)或非常量靜態局部變量: 這些變量在內存中只有一份拷貝,如果多個執行流同時修改它們,會導致數據不一致。
  2. 不返回指向靜態數據的指針: 調用者可能會修改這些數據,影響其他執行流。
  3. 僅使用調用者提供的數據或自己棧上的局部變量: 每個執行流(線程/函數調用實例)都有自己的棧空間,局部變量是獨立的。
  4. 不調用不可重入的函數: 如果它調用的函數本身是不可重入的(比如使用了全局狀態),那么它自己也就變得不可重入了。
  5. 不修改自身的代碼: 通常這不是問題,但某些特殊場景(如自修改代碼)需要考慮。
  6. 不依賴外部硬件狀態(除非以原子方式訪問): 比如多個執行流同時操作同一個硬件寄存器可能造成沖突。

volatile

volatileC語言中的一個關鍵字,這個關鍵字的在之前的學習中并沒有使用過;

volatile關鍵字用來修飾一個變量,其作用就是,告訴編譯器該變量的值可能會變化,讓編譯器不要對其進程優化,讓CPU每次訪問該變量的值都從內存中獲取。

#include <iostream>
#include <signal.h>
#include <unistd.h>int flag = 0;
void handler(int signum)
{std::cout << "change flag 0 -> 1" << std::endl;flag = 1;
}
int main()
{signal(2, handler);int cnt = 0;while (!flag){std::cout << "flag :" << flag << std::endl;sleep(1);}return 0;
}

在上述代碼中,main函數while(!falg),當flag = 0時,循環一直在進行;

當進程收到2號信號時,執行自定義處理handler方法,修改falg

預期結果就是:進程在收到2號信號時,flag修改為1,循環就結束了。

正常來說,CPU在執行進程時,訪問flag變量都是從內存中讀取;而在main函數中并沒有修改flag變量,一些編譯器就會對其進行優化,將flag變量直接寫入CPU寄存器中。

volatile修飾變量就是告訴編譯器不要進行優化,每次都從內存中讀取變量的值。

SIGCHLD信號

這里簡單了解一些SIGCHLD信號;

SIGCHLD信號是子進程退出時,操作系統給父進程發送的一個信號。

我們知道,子進程在退出時,會進入僵尸狀態,等待父進程回收退出信息;就要父進程等待子進程。

而如果我們不關心子進程的退出信息,我們就可以將父進程對于SIGCHILD信號的處理方式設置成SIG_IGN

這樣子進程在退出時,操作系統給父進程發送SIGCHLD信號,父進程SIG_IGN,此時子進程的task_struct就會立即被回收,不需要父進程等待。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{signal(SIGCHLD, SIG_IGN);int id = fork();if(id < 0)exit(1);else if(id == 0){printf("child process pid : %d\n",getpid());sleep(1);exit(1);}int cnt = 3;while(cnt--){printf("parent process pid : %d\n",getpid());sleep(1);}return 0;
}

在這里插入圖片描述

可以看到,子進程退出后,父進程沒有等待wait;子進程也沒有出現僵尸狀態。

在這里插入圖片描述

但是,可以看到進程對于SIGCHLD信號的處理方式是Ign;那為什么不調用signal(SIGCHLD, SIG_IGN),父進程不等待,子進程就要進入僵尸狀態呢?

這里,進程對于SIGCHLD信號的處理方式是默認處理SIG_DFL,而默認處理的方式是Ign

SIG_IGN不一樣,操作系統設置成默認處理SIG_DFL,默認處理的方式是Ign;這樣在子進程退出后,父進程就可以隨時獲取子進程的退出信息,回收子進程了。

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

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

相關文章

twikitFKS: 基于 twikit 2.3.1 的改進版本

twikitFKS: 基于 twikit 2.3.1 的改進版本 項目概述 關于 twikit twikit 是一個優秀的 Twitter API 爬蟲庫&#xff0c;它的核心優勢在于無需 API Key即可訪問 Twitter 功能。通過網頁爬蟲技術&#xff0c;twikit 實現了&#xff1a; 發布推文和媒體內容搜索推文和用戶獲取…

C Primer Plus 第6版 編程練習——第9章(下)

7.編寫一個函數&#xff0c;從標準輸入中讀取字符&#xff0c;直到遇到文件結尾。程序要報告每個字符是否是字母。如果是&#xff0c;還要報告該字母在字母表中的數值位置。例如&#xff0c;c和C在字母表中的位置都是3。合并一個函數&#xff0c;以一個字符作為參數&#xff0c…

如何用文思助手改好一篇爛材料

在日常工作中&#xff0c;我們常常會遇到這樣的問題&#xff1a;因為工作要使用到之前寫的文章再看發現內容雜亂無章、或者收到的一些返稿內容質量差&#xff0c;不修改無法使用。但其實它們可能只是缺少了系統性的梳理與打磨。今天我們就來聊一聊&#xff0c;如何對一些不滿意…

VSCODE常規設置

摘要&#xff1a;用于新下載的vscode設置一些個人化的操作在 "Files: Auto Save" 下拉菜單中&#xff0c;選擇你想要的自動保存模式。常見的選項包括&#xff1a;"off"&#xff1a;禁用自動保存。 "afterDelay"&#xff1a;在你停止編輯一段時間…

2025秋招突圍戰:AI智能監考如何重構遠程筆試公平防線?

2025秋招季即將來臨&#xff0c;企業校招規模預計突破百萬量級&#xff0c;遠程筆試成為主流篩選方式。然而&#xff0c;傳統監考模式暴露出作弊行為難追溯、人力成本過高、數據維度單一等痛點&#xff0c;讓HR陷入“效率與公平”的兩難困境。牛客AI智能監考系統&#xff0c;通…

Python 基礎語法與數據類型(十三) - 實例方法、類方法、靜態方法

文章目錄1. 實例方法 (Instance Methods)1.1 特點與語法1.2 實例方法示例2. 類方法 (Class Methods)2.1 特點與語法2.2 類方法示例3. 靜態方法 (Static Methods)3.1 特點與語法3.2 靜態方法示例4. 三種方法的對比總結總結練習題練習題答案創作不易&#xff0c;請各位看官順手點…

Wireshark的安裝和基本使用

文章目錄一、Wireshark介紹二、Wireshark安裝三、Wireshark講解1.界面介紹&#xff08;1&#xff09;分組列表&#xff08;2&#xff09;分組詳情&#xff08;3&#xff09;分組字節流一、Wireshark介紹 Wireshark 是一款開源的網絡協議分析工具&#xff0c;能夠捕獲、過濾和分…

[yotroy.cool] Git 歷史遷移筆記:將 Git 項目嵌入另一個倉庫子目錄中(保留提交記錄)

個人博客https://www.yotroy.cool/&#xff0c;感謝關注&#xff5e; 圖片資源可能顯示不全&#xff0c;請前往博客查看哦&#xff01; 說來慚愧&#xff0c;這篇是AI幫助我解決實際問題后&#xff0c;又生成的一篇博客&#xff0c;效率特別高。 在開發中&#xff0c;我們常會…

91套商業策劃創業融資計劃書PPT模版

創業融資計劃書PPT模版&#xff0c;商業項目技術書PPT模版&#xff0c;商業創業計劃書&#xff0c;商業融資企業宣傳PPT模版&#xff0c;活動策劃方案書PPT模版&#xff0c;IOS風格商業計劃書PPT模版 91套商業策劃創業融資計劃書PPT模版&#xff1a;https://pan.quark.cn/s/739…

探秘阿里云通義九子:解鎖AI無限可能

通義九子初印象在當今人工智能飛速發展的時代&#xff0c;阿里云通義九子宛如一顆璀璨的明星&#xff0c;閃耀在 AI 的浩瀚天空中。作為阿里云推出的一系列強大的人工智能模型&#xff0c;通義九子在自然語言處理、圖像生成、智能客服等多個領域展現出了卓越的能力&#xff0c;…

Python網絡爬蟲之requests庫

目錄 一.網絡爬蟲的介紹 1.網絡爬蟲庫 2.robot.txt規則 二.requests庫 1.requests庫的安裝 2.get()函數 3.Response對象 Response的屬性 設置編碼 返回網頁內容 text() content() 三.提交信息到網頁 post()函數 四.會話與代理服務器 一.網絡爬蟲的介紹 1.網絡爬蟲…

區塊鏈技術詳解:從原理到應用

引言 區塊鏈作為一項顛覆性技術&#xff0c;已從加密貨幣的基石演變為重塑多個行業的創新引擎。本文旨在深入解析其核心原理、關鍵特性、技術架構、主流應用及未來挑戰。一、 區塊鏈核心概念&#xff1a;超越加密貨幣的分布式賬本 本質定義&#xff1a; 區塊鏈是一個去中心化、…

用Finalshell連接服務器后出現文件目錄不顯示,且刷新報錯空指針問題記錄

修改SSH配置?編輯sshd_config?使用管理員權限編輯/etc/ssh/sshd_config文件&#xff0c;找到Subsystem相關配置。原配置為Subsystem sftp /usr/libexec/openssh/sftp-server使用“i”編輯文件將修改為Subsystem sftp internal-sftp修改完成后使用Esc命令&#xff0c;退出編輯…

C語言:游戲代碼分享

小游戲分享 目錄 小游戲分享 1.井字棋游戲 2.簡單計算器游戲 3.猜單詞 4.石頭剪刀布游戲 5.猜數字游戲 1.井字棋游戲 「33 棋盤上的思維博弈&#xff01;與好友輪流落子&#xff0c;搶占先機&#xff0c;連成一線即可獲勝。簡單規則蘊含無限策略&#xff0c;展現你的戰術…

深度學習入門-卷積神經網絡(CNN)(下)

1-4、 深度學習入門-卷積神經網絡&#xff08;CNN&#xff09;&#xff08;上&#xff09;-CSDN博客 5、 卷積神經網絡&#xff08;CNN&#xff09;的實現 簡單網絡的構成是“Convolution - ReLU - Pooling - Affine - ReLU - Affine - Softmax”&#xff1a; 相關代碼&#…

Java 大視界 -- Java 大數據在智能交通智能公交站臺乘客流量預測與服務優化中的應用(349)

Java 大視界 -- Java 大數據在智能交通智能公交站臺乘客流量預測與服務優化中的應用&#xff08;349&#xff09;引言&#xff1a;正文&#xff1a;一、Java 全場景韌性調度系統&#xff08;新增極端天氣 車型適配&#xff09;1.1 極端天氣&#xff1a;暴雪 / 臺風的分鐘級響應…

數論內容主要包括哪些

數論&#xff08;Number Theory&#xff09;是數學中研究整數的性質及其相互關系的一個分支&#xff0c;被譽為“數學中的皇后”。它歷史悠久&#xff0c;內容豐富&#xff0c;既包含許多初等、直觀的問題&#xff0c;也涉及高深、抽象的理論。數論的主要內容包括以下幾個方面&…

springboot打包二次壓縮Excel導致損壞

springboot打包二次壓縮Excel導致損壞開發時&#xff0c;將Excel文件放到resources下&#xff0c;通過類加載器流讀取&#xff0c;返回api用于下載該Excel文件。我發現這樣下載的Excel被損壞了&#xff0c;無法打開&#xff0c;推測是springboot打包插件默認對resources下的所有…

huggingface筆記

1. huggingface的下載目錄 ~/.cache/huggingface 2. 如何修改hugging face的模型默認下載地址 huggingface的默認下載路徑在~/.cache/huggingface/hub/&#xff0c;但模型數據占用空間往往很大&#xff0c;可以用以下方法修改默認下載路徑。 方法一&#xff1a;在linux中指定環…

Redis3:Redis數據結構與命令全解析

目錄 1、redis數據結構介紹 1.1命令學習方式 1.1.1命令行查詢 2、redis的通用命令 2.1查找常見的通用命令 2.2常見的通用命令&#xff1a; 3、String類型 3.1String類型的常見命令 3.2Key的層級格式 3.2.1Key的結構 4、Hash類型 4.1Hash類型 4.2Hash類型常見命令 …