Linux|信號

Linux|信號

  • 信號的概念
  • 信號處理的三種方式
    • 捕捉信號的System Call -- signal
  • 1.產生信號的5種方式
  • 2.信號的保存
    • 2.1 core 標志位
  • 2.信號的保存
    • 2.1 對pending 表 和 block 表操作
    • 2.2 阻塞SIGINT信號 并打印pending表例子
  • 捕捉信號
    • sigaction 函數
    • 驗證當前正在處理某信號,則該信號會自動被屏蔽
    • 驗證當前信號被處理完之后,會自動解除屏蔽
    • 地址空間中操作系統態
    • 談談鍵盤輸入的過程
    • 兩個深刻的問題
      • 如何操作系統是怎么運行的
      • 如何理解系統調用
    • 可重入函數
    • volatile
    • sigchild信號

信號的概念

信號:是進程之間異步通知的一種方式,屬于軟中斷。
所謂異步就是 a 和 b 之間沒有聯系,比如同學a 去上廁所了,老師b還是繼續講課,這稱為異步。

信號處理的三種方式

一般情況下是三選一

  1. 忽略此信號
  2. 執行該信號的默認處理動作
  3. 提供一個信號處理函數,要求內核在處理該信號時切換到用戶態執行這個處理函數,這種方式稱為捕捉(Catch)一個信號

捕捉信號的System Call – signal

每個信號都有一個編號和一個宏定義名稱,這些宏定義可以在signal.h中找到,例如其中有定 義 #define SIGINT 2

在這里插入圖片描述

sighandler_t signal(int signum, sighandler_t handler);
當我們在鍵盤中 按ctrl + c 的時候 就會發送一個SIGINT信號,
我們可以用 signal 這個系統調用驗證

#include <iostream>
#include <unistd.h>
#include <signal.h>
void hander(int sig)
{std::cout<<"catch sig:"<< sig<<std::endl;
}
int main()
{signal(2,hander);while(true)return 0;
}

有同學會想我把所有的信號都捕捉了,那個這個進程是不是就刀槍不入了?不是的 因為9號信號 無法捕捉

1.產生信號的5種方式

1. 通過 kill 命令,向指定的進程發信號
2. 通過鍵盤 ctrl + c
3. 系統調用 kill
在這里插入圖片描述

raise(sign) 和 kill(getpid(),sign) 是等價的
alrm 也可以產生信號 alrm的返回值是上一個鬧鐘的剩余時間
同一個進程同一個時間只能有一個鬧鐘!

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
void hander(int sig)
{std::cout<<"catch sig:"<< sig<<std::endl;
}
int main()
{signal(2,hander);//kill(getpid(),2);raise(2);sleep(3);return 0;
}

4.軟件條件
比如 管道 我們讀端關閉 , 寫端還在寫,那么就會產生一個SIGPIPE的信號。
5. 異常
a.

void hander(int sg)
{std:: cout<< "捕捉到:"<<sg<<std::endl;
}
int main()
{signal(8,hander);int b = 10 / 0;return 0;
}

在這里插入圖片描述
可能有同學會問為什么會一直死循環打印捕捉到的8號信號呢?
當處理器檢測到除法錯誤時,它會暫停正常的指令流,保存當前的狀態(包括程序計數器和其他寄存器的內容),然后跳轉到一個預定義的地址來處理這個異常。這個地址指向的是操作系統的異常處理程序,它可以記錄錯誤、終止進程或采取其他恢復措施,由于進程沒有退出,又恢復當前的狀態,到cpu中 ,cpu中的溢出標記位又置為1了。(這也回答cpu是怎么檢測到除以0的)總的來說就是因為進程一直被調度,所以才出現死循環的情況。

終止進程的本質:釋放進程的上下文數據,報告溢出標志數據或其他異常數據
b. 野指針問題:
CR3 + MMU : 將虛擬地址轉換為物理地址
CR2:保存主要用于存儲最近一次發生的頁面錯誤(page fault)時的線性地址。
在這里插入圖片描述
當異常的時候,操作系統檢測到CR2中的地址,開始發送信號。

2.信號的保存

2.1 core 標志位

在這里插入圖片描述
當時在進程控制時 waitpid 函數中的 status參數 core dump 標志位 我們現在就馬上知道什么意思了。當程序被信號殺死時,會生成一個core的debug文件。 這個core標記位 ,為0不允許生成,為1運行生成debug文件。
在這里插入圖片描述
在云服務上 生成這個core文件的功能默認是被關閉的
ulimit - a 查看core file size 的大小
在這里插入圖片描述
ulimit -c 【size】 設置一下就好了
也有 可能 生成的core 文件不在當前目錄
echo ./core > /proc/sys/kernel/core_pattern 就歐克啦
在這里插入圖片描述
一重啟就會生成一個core.進程號的文件 如果無限制的重啟 就會生成非常多的core文件 所以云服務器就把這個功能關閉了
在這里插入圖片描述
調試的時候,我們core-file core文件 把這個debug文件加載進去,調試器就直接顯示出錯的那一行了!
在這里插入圖片描述

#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>int sum(int star, int end)
{int ret = 0;for (int i = star; i <= end; i++){ret /= 0;ret += i;}return ret;
}
int main()
{pid_t id = fork();if(id == 0){sleep(1);sum(1, 100);exit(0);}int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid > 0){printf("exit code: %d, sig: %d, core dump:%d\n",(status >> 8) &0xff, status &0x7f,(status >> 7) &1);}return 0;
}

在這里插入圖片描述
當我們把ulimit -c設置為 0時 coredump 標記位就為0了 表示 不生成core dump(核心轉儲)文件
在這里插入圖片描述

2.信號的保存

信號的保存就保存在這三張表中,block表,peding表,handler表。
每個信號都有兩個標志位分別表示阻塞(block)和未決(pending),還有一個函數指針表示處理動作。信號產生時,內核在進程控制塊中設置該信號的未決標志,直到信號遞達才清除該標志。
執行信號處理的動作稱為信號的遞達。
信號產生到遞達之間稱為未決
如果一個信號被阻塞了,那么它永遠未決。
在這里插入圖片描述
我們用的signal方法sighandler_t signal(int signum, sighandler_t handler); 其中 我們寫的handler 就是把函數地址寫進對應的handler表下標中 。
兩張位圖+函數指針數組 == 讓進程識別信號

2.1 對pending 表 和 block 表操作

先介紹幾個函數

#include <signal.h>
// 清空位圖
int sigemptyset(sigset_t *set);
// 所有bit位全為1
int sigfillset(sigset_t *set);
// 把某一bit位置為1
int sigaddset (sigset_t *set, int signo);
// 把某一bit位置為0
int sigdelset(sigset_t *set, int signo);
// 判斷某一比特位是不是1
int sigismember(const sigset_t *set, int signo); 

signal.h 給我們提供了 用戶級別的位圖,這些函數可以用來操作這個位圖 sigset_t

//調用函數sigprocmask可以讀取或更改進程的信號屏蔽字(阻塞信號集)。
int sigprocmask(int how, const sigset_t *set, sigset_t *oset); 
// 獲取pending 表
int sigpending (sigset_t * set);

2.2 阻塞SIGINT信號 并打印pending表例子

// 利用上面的函數,我們就是驗證 某一信號被阻塞后,是否一直未決
#include <iostream>
#include <signal.h>
#include <unistd.h>
void PrintPending( sigset_t & pending)
{for(int sig = 31; sig >= 1; sig--){if(sigismember(&pending,sig)){std::cout<<1;}else{std::cout<<0;}}std::cout<<std::endl;
}int main()
{sigset_t block_set , old_set;sigemptyset(&block_set);sigemptyset(&old_set);sigaddset(&block_set , SIGINT);// sigprocmask(SIG_BLOCK,&block_set,&old_set);while(true){sigset_t pending;sigpending(&pending);PrintPending(pending);sleep(1);}return 0;
}

捕捉信號

如果信號的處理動作是用戶自定義函數,在信號遞達時就調用這個函數,這稱為捕捉信號
信號可能不會立即被處理,而是在合適的時候處理
,這個合適的時候指的是,從用戶態返回內核態的時候進行處理。
用戶態:執行我們自己的數據和代碼的時候
內核態:執行操作系統的代碼和數據的時候
在這里插入圖片描述
當信號的處理動作是自定義的信號處理函數時才返回時先到用戶態再從內核態到用戶態(因為hander方法 和 main 函數不是調用關系并不能直接返回)。
如果是默認 則直接殺死進程了。 忽略則 除了修改pending 表 由 1 變為 0,其他什么也不干。
舉例:
戶程序注冊了SIGQUIT信號的處理函數sighandler。 當前正在執行main函數,這時發生中斷或異常切換到內核態。 在中斷處理完畢后要返回用戶態的main函數之前檢查到有信號
SIGQUIT遞達。 內核決定返回用戶態后不是恢復main函數的上下文繼續執行,而是執行sighandler函 數,sighandler
和main函數使用不同的堆棧空間,它們之間不存在調用和被調用的關系,是 兩個獨立的控制流程。 sighandler函數返
回后自動執行特殊的系統調用sigreturn再次進入內核態。 如果沒有新的信號要遞達,這次再返回用戶態就是恢復main函數的上下文繼續執行了。

sigaction 函數

**int sigaction(int signum, const struct sigaction act, struct sigaction oldact);
和 signal一樣都是捕捉信號的,它有一個同名的結構體,但這個結構體我們只關心ssiginfo_t 這個函數指針方法字段在這里插入圖片描述

void handler(int signal)
{std::cout<<"捕捉到:"<<signal<<std::endl;// while(true)// {//     sigset_t pending;//     sigpending(&pending);//     Print(pending);//     sleep(1);// }exit(1);
}int main()
{struct sigaction act, oact;act.sa_flags = 0;//在這種情況下,信號處理將遵循默認的行為,//也就是說,信號處理函數將作為一個普通的函數執行,//而不會觸發任何 sa_flags 標志所定義的特殊行為。act.sa_handler = handler;sigemptyset(&act.sa_mask);//sigaddset(&act.sa_mask,3); // 順帶屏蔽三號信號sigaction(2,&act,&oact);while(true){std::cout<<"pid: "<<getpid()<<std::endl;sleep(1);}    return 0;
}

在這里插入圖片描述

驗證當前正在處理某信號,則該信號會自動被屏蔽

我們在hander方法中一直sleep,不退出hander方法,我們再按ctrl + c信號也不會被處理了。這就驗證了當前信號正在被處理,則該信號會被自動屏蔽。
在這里插入圖片描述

驗證當前信號被處理完之后,會自動解除屏蔽

我們設置hander方法睡三秒自動退出。 退出之后又可以捕捉到2號信號則證明了該結論
在這里插入圖片描述

地址空間中操作系統態

內核級頁表所有進程共享一份用戶級頁表每一個進程都有一份。操作系統的代碼數據都通過內核級頁表映射在物理內存中。
在這里插入圖片描述

談談鍵盤輸入的過程

操作系統怎么知道鍵盤摁下了? 是一直問鍵盤嗎?當然不是,那不然太浪費cpu資源了
在這里插入圖片描述
每一個硬件都有一個中斷號,硬盤也不例外,當按下一個鍵后,通過8529這個芯片向cpu 發出硬件中斷,某一個寄存器上就有了鍵盤的中斷號,再在中斷向量表中查詢對應的鍵盤讀入方法~這樣就完成了cpu知道鍵盤輸入的一個過程。
我們學習的信號就是模擬硬件中斷實現的!

兩個深刻的問題

如何操作系統是怎么運行的

操作系統調用進程誰由來調度操作系統呢?
硬件上有一個時鐘,時鐘到了就通過中斷提醒操作系統該檢測進程的時間片,時間片到了就切換進程,否則什么也不做
在這里插入圖片描述

如何理解系統調用

  1. 有一個函數指針數組,通過下標 可以找到系統調用,這個下標我們稱為系統調用號。
  2. 我們使用系統調用如fork時,會產生內部中斷(陷阱),執行系統調用的方法,讓cpu找這個函數指針數組。eax 中保存這個函數系統調用號,然后cpu就找到這個系統調用了

可重入函數

在這里插入圖片描述
像上例這樣,insert函數被不同的控制流程調用,有可能在第一次調用還沒返回時就再次進入該函數,這稱為重入,insert函數訪問一個全局鏈表,有可能因為重入而造成錯亂,像這樣的函數稱為 不可重入函數

volatile

#include <iostream>
#include <signal.h>
int gflag = 0;
void changeData(int signo)
{std::cout<<"gflg:0 -> 1"<<std::endl;gflag = 1;
}
int main()
{signal(2,changeData);while(!gflag);std::cout<<"process quit!"<<std::endl;return 0;
}

在這里插入圖片描述
當我們用編譯器O1的優化時,main函數 里面又沒有修改 gflag的值,于是編譯器把內存中的值拷貝到寄存器后,就只看寄存器中的值了。
在這里插入圖片描述
怎么解決這個問題呢?
我們可以在gval前 加一個volatile關鍵字 保證內存的可見性就行了。

sigchild信號

子進程退出的時候會給父進程發送一個sigchild信號

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>
void notice(int sig)
{std::cout<<"I am fatherprocess,pid: "<<getpid()<<std::endl;std::cout<<"get sig:"<<sig<<std::endl;
}
int main()
{signal(SIGCHLD,notice);pid_t id = fork();if(id == 0){std::cout<<"I am childprocess,pid: "<<getpid()<<std::endl;sleep(3);exit(1);}sleep(100);return 0;
}

在這里插入圖片描述

如果不關心 子進程的退出信息則可以把SIGCHLD 的捕捉動作改為SIG_IGN

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>int main()
{signal(SIGCHLD,SIG_IGN);pid_t id = fork();if(id == 0){int cnt = 5;while(cnt--){std::cout<<"child process runing"<<std::endl;std::cout<<"cnt:"<<cnt<<std::endl;sleep(1);}exit(1);}while(true){std::cout<<"father process runing"<<std::endl;sleep(1);}return 0;
}

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

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

相關文章

數據庫SQL Server常用字符串函數

文章目錄 字符串函數 字符串函數 CONCAT:拼接字符串 CONCAT(COLUMN1,_,COLUMN2) AS COLCONVERT&#xff1a;轉換數據類型 CONVERT(data_type(length),data_to_be_converted,style)例如&#xff1a;CONVERT(VARCHAR(10),GETDATE(),110) SUBSTRING()&#xff1a;從字符串中返回…

java項目總結5

1.單列集合頂層接口Collction 集合體系結構 注意&#xff1a;因為Collection定義的方法是共性的&#xff0c;使用不能通過搜引來刪除&#xff0c;只能通過元素的對象進行刪除&#xff0c;返回值是boolean類型。例如我添加了"aaa"進List集合&#xff0c;刪除則要對象…

STM32-01 推挽輸出-點亮LED

本文以STM32中點亮LED為例&#xff0c;解讀推挽輸出的原理 推挽輸出介紹 所謂的推挽輸出&#xff0c;就是通過控制輸出控制模塊&#xff0c;打開或者關閉P-MOS或者N-MOS。 ─ 推挽模式下&#xff1a;輸出寄存器上的’0’激活N-MOS&#xff0c;而輸出寄存器上的’1’將激活P-M…

局部靜態變量實現的單例存在多個對象

文章目錄 背景測試代碼運行測試嘗試打開編譯器優化進一步分析 背景 業務中出現日志打印失效&#xff0c;發現是因為管理日志對象的單例在運行過程中存在了多例的情況。下面通過還原業務場景來分析該問題。 測試代碼 /* A.h */ #ifndef CALSS_A #define CALSS_A#include <…

打造屬于自己的腳手架工具并發布到npm倉庫

一、創建項目 使用 npm init -y 創建項目創建項目入口文件 index.js在 package.json 中添加 bin 字段使用 npm link 命令將文件映射至全局&#xff0c;使可以在本地測試 zp 命令 // "zp" 為用于全局執行腳手架的命令&#xff0c;vue-cli中使用的是vue命令 "bi…

pyecharts可視化案例大全(11~20)

pyecharts可視化案例大全(11~20) 十一、設置動畫效果十二、直方圖帶視覺組件十三、設置漸變色(線性漸變)十四、設置漸變色(徑向漸變)十五、設置分割線十六、設置分隔區域十七、面積圖十八、堆疊面積圖十九、自定義線樣式二十、折線圖平滑處理十一、設置動畫效果 在圖表加載前…

【AI原理解析】—主成分分析(PCA)原理

目錄 一、PCA的思想 二、PCA的步驟 三、關鍵概念 四、PCA的優勢與應用 PCA&#xff08;主成分分析&#xff0c;Principal Component Analysis&#xff09;是一種廣泛使用的數據降維算法&#xff0c;它通過線性變換將原始數據轉換為一組各維度線性無關的表示&#xff0c;從而…

iOS應用的內存優化

對一個 iOS 項目進行內存優化&#xff0c;可以從多個方面入手&#xff0c;確保應用在不同場景下都能高效穩定地運行。以下是一些具體的內存優化措施和詳細說明&#xff1a; 1. 自動引用計數&#xff08;ARC&#xff09;管理 1.1 避免循環引用 循環引用會導致內存泄漏。使用 …

低代碼平臺的設計模式介紹

低代碼平臺是一種快速交付應用程序的開發工具&#xff0c;主要通過圖形拖拽用戶界面、應用配置界面&#xff0c;使開發者能夠以最少的手動編碼&#xff0c;或者不需要代碼快速交付應用。這種平臺的核心優勢在于提高開發速度和降低技術門檻&#xff0c;使得非技術背景的用戶也能…

基于java+springboot+vue實現的旅游管理系統(文末源碼+lw+ppt)23-402

研究的內容 當下流行的WPS、Word等辦公軟件成為了人們耳熟能詳的系統&#xff0c;但一些更加專業性、性能更加強大的網絡信息工具被人們“埋沒”在互聯網的大海中。甘肅旅游管理系統是一個便于用戶查看熱門景點、酒店信息、推薦線路、旅游攻略、景點資訊等&#xff0c;管理員進…

【Python基礎篇】你了解python中運算符嗎

文章目錄 1. 算數運算符1.1 //整除1.2 %取模1.3 **冪 2. 賦值運算符3. 位運算符3.1 &&#xff08;按位與&#xff09;3.2 |&#xff08;按位或&#xff09;3.3 ^&#xff08;按位異或&#xff09;3.4 ~&#xff08;按位取反&#xff09;3.5 <<&#xff08;左移&#…

HTML 【實用教程】(2024最新版)

核心思想 —— 語義化 【面試題】如何理解 HTML 語義化 ?僅通過標簽便能判斷內容的類型&#xff0c;特別是區分標題、段落、圖片和表格 增加代碼可讀性&#xff0c;讓人更容易讀懂對SEO更加友好&#xff0c;讓搜索引擎更容易讀懂 html 文件的基本結構 html 文件的文件后綴為 …

【高錄用、快檢索、過往5屆均已檢索、SPIE 出版】第六屆無線通信與智能電網國際會議(ICWCSG 2024)

隨著科技的飛速發展和能源需求的日益增長&#xff0c;智能電網技術逐漸成為電力行業的重要發展方向。與此同時&#xff0c;無線通信技術在近年來也取得了顯著的進步&#xff0c;為智能電網的發展提供了強有力的支持。為了進一步推動無線通信與智能電網的結合與發展&#xff0c;…

Vue3 對于內嵌Iframe組件進行緩存

1&#xff1a;應用場景 對于系統內所有內嵌iframe 的頁面均通過同一個路由/iframe, 在router.query內傳入不同src 參數&#xff0c;在同一組件內顯示iframe 內嵌頁面&#xff0c;對這些頁面分別進行緩存。主要是通過v-show 控制顯示隱藏從而達到iframe 緩存邏輯 2&#xff1a…

Github 2024-07-03 C開源項目日報 Top9

根據Github Trendings的統計,今日(2024-07-03統計)共有9個項目上榜。根據開發語言中項目的數量,匯總情況如下: 開發語言項目數量C項目9Java項目1Python項目1顯示和控制你的 Android 設備 創建周期:2416 天開發語言:C, Java協議類型:Apache License 2.0Star數量:105222 個…

學IT上培訓班真的有用嗎?

在學習IT技術的過程中&#xff0c;你是否也被安利過各種五花八門的技術培訓班&#xff1f;這些培訓班都是怎樣向你宣傳的&#xff0c;你又對此抱有著怎樣的態度呢&#xff1f;在培訓班里學技術&#xff0c;真的有用嗎&#xff1f; 一、引入話題 IT行業是一個快速發展和不斷變化…

C++初學者指南-4.診斷---未定義行為檢測器

C初學者指南-4.診斷—未定義行為檢測器 未定義行為檢測器(UBSAN) 適用編譯器&#xff1a;clang,g在運行時檢測許多類型的未定義行為 解引用空指針從未對齊的指針讀取整數溢出被0除 … 在代碼中加入額外的指令:在調試構建中增加運行時約25% 示例&#xff1a;有符號整形溢出 …

Git在多人開發中的常見用例

前言 作為從一個 svn 轉過來的 git 前端開發&#xff0c;在經歷過git的各種便捷好處后&#xff0c;想起當時懵懂使用git的膽顫心驚&#xff1a;總是害怕用錯指令&#xff0c;又或者遇到報錯就慌的場景&#xff0c;想起當時查資料一看git指令這么多&#xff0c;看的頭暈眼花&am…

深度學習原理與Pytorch實戰

深度學習原理與Pytorch實戰 第2版 強化學習人工智能神經網絡書籍 python動手學深度學習框架書 TransformerBERT圖神經網絡&#xff1a; 技術講解 編輯推薦 1.基于PyTorch新版本&#xff0c;涵蓋深度學習基礎知識和前沿技術&#xff0c;由淺入深&#xff0c;通俗易懂&#xf…

家里老人能操作的電視直播軟件,目前能用的免費看直播的電視軟件app,適合電視和手機使用!

2024年許多能看電視直播的軟件都不能用了&#xff0c;家里的老人也不會手機投屏&#xff0c;平時什么娛樂都沒有了&#xff0c;這真的太不方便了。 很多老人并不喜歡去買一個廣電的機頂盒&#xff0c;或者花錢拉有線電視。 現在的電視大多數都是智能電視&#xff0c;所以許多電…