Linux信號
- 信號概述
- 信號的記錄與發送
- 信號的產生
- signal函式(自定義行為)
- 產生信號
- Core Dump
- 程式例外產生信號
- status(16位)
- ctrl+z信號
- 系統呼叫產生信號
- kill
- raise
- abort
- 軟體條件產生的信號
- alarm
- 信號的保存
- 修改位圖
- ①sigprocmask
- ②sigpending
- 信號的處理
- sigaction
- volatile關鍵字
信號概述
像上課鈴聲這種信號,我們識別接收后,知道該上課了,這是我們后天學習養成的默認意識
在行程收到信號后,它是知道該怎么做的 ,程式員內置了默認的處理行為
行程的運行跟信號的產生屬于異步關系:
1.行程不一定立刻去處理已經到來的信號
2.如果行程在處理優先級更高的事情,可以暫時不處理信號,等到合適的時候再處理,
3.會用某種方式記錄下已經到來的但沒處理的信號,以便在空閑的時候處理這些信號
異步:二者之間互不影響
同步:二者之間相互影響
處理信號的三種方式:
1.默認行為
2.提供信號處理函式,要求內核在處理該信號時切換到用戶態來執行這個處理函式,稱為捕捉信號
3.忽略
kill -l 查看信號

總共有62個信號(1-31普通信號,34-64實時信號)

信號的記錄與發送
信號是在行程的task_struct中記錄的,通過位圖來記錄是否產生信號
所以行程收到信號,本質是作業系統修改了行程中的信號位圖(只能是OS)
作業系統是軟硬體資源的管理者
信號的產生
signal函式(自定義行為)

示例:
ctrl c(2號信號) 

通過鍵盤輸入ctrl+c(2號信號)來給行程發送信號
ctrl c(鍵盤產生的信號)只能發送給前臺行程,一個命令后面加個&可以放到后臺運行,這樣Shell不必等待行程結束就可以接受新的命令,啟動新的行程,
可以用kill發送信號給后臺行程
并不是所有的信號都能捕捉,比如9號就不能(全部捕捉就意味著可能不能殺死行程)
產生信號
Core Dump
SIGINT(Term)的默認處理動作是終止行程
SIGQUIT(Core)的默認處理動作是終止行程并且Core Dump

用3號信號關掉行程的時候,會進行核心轉儲
查看系統當中的系統資源:ulimit - a

云服務器下的核心轉儲是默認關閉的(why:每次運行程式掛掉都會在磁盤產生不小的core.pid檔案)
我們把它打開:

再次運行程式向行程發出三號信號(Ctrl+\)

多產生一個core檔案,5865是發生核心轉儲的行程ID

核心轉儲:
代碼運行中出錯時,將行程記憶體中的核心資料轉儲到磁盤上,生成core.pid檔案
目的是為了除錯定位問題
程式例外產生信號
利用core檔案進行事后除錯除0例外

使用命令core-file core.pid 查看錯誤資訊
可以看到被8號信號(SIGFPE)終止,15行報錯

行程為什么會崩潰
程式崩潰的本質是收到了OS發送的信號
行程為什么會收到信號
當程式發生某種錯誤時,一定會在硬體層面上有所表現,進而被OS識別,向該行程發出信號
status(16位)

子行程正常退出:


子行程例外退出:


子行程收到8號信號,并且coredump為1,說明運行時程式崩潰時core dump了
ctrl+z信號
ctrl+z(20號信號)暫停行程,把行程放到后臺
jobs:查看后臺行程
fg 序號 將后臺恢復到前臺

系統呼叫產生信號
kill

模擬實作kill函式(利用命令列引數)
命令列引數:

將字串轉為int



可以看到通過命令列引數,在程式里面進行系統呼叫kill,依然產生了信號
raise

作用:自己給自己發信號
示例:


abort

abort函式使當前行程接收到信號而例外終止
特性:就像exit函式一樣,abort函式總是會成功的,所以沒有回傳值
示例:


軟體條件產生的信號
SIGPIPE是一種由軟體條件產生的信號
alarm

在傳入時間后,向行程發送14號信號

不像abort,捕獲后并沒有終止行程

每隔一秒列印遞增的count:
每次alarm發出信號捕獲后,有設定新的鬧鐘,就會一直列印


信號的保存
1.實際執行信號的處理動作稱為信號遞達
2.信號從產生到遞達之間的狀態,稱為信號未決
3.行程可以選擇阻塞某個信號
4.被阻塞的信號產生時將保持在未決狀態,直到行程解除對此信號的阻塞,才執行遞達的動作
收到信號后:
所以在阻塞時(1到31號信號)收到多次該信號只處理一次
修改位圖
sigset_t(信號集)
作用:用于描述行程的block位圖,pending位圖的信號集
#include <signal.h>
int sigemptyset(sigset_t *set);//清空
int sigfillset(sigset_t *set);// 全部置1
int sigaddset (sigset_t *set, int signo);//添加信號到信號集
int sigdelset(sigset_t *set, int signo);//洗掉信號到信號集
int sigismember(const sigset_t *set, int signo);//判斷信號是否存在

系統呼叫讓設定能夠修改PCB里面的內容:
①sigprocmask
sigprocmask可以讀取或更改行程的信號屏蔽字(阻塞信號集)
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
回傳值:若成功則為0,若出錯則為-1
how:如何修改當前信號集
| SIG_BLOCK | 添加set中的包含的信號到信號集 |
|---|---|
| SIG_UNBLOCK | 解除信號集中set所包含的信號 |
| SIG_SETMASK | 將set拷貝給當期阻塞信號集 |
set:用set信號集來修改當前阻塞信號集
oset:阻塞信號集會先備份到oset里面,是輸出型引數,方便恢復信號集
②sigpending

獲取當前行程的未決信號集,通過set引數傳出(輸出型引數),呼叫成功則回傳0,出錯則回傳-1
示例:


可以看出:先屏蔽了2號信號,想行程發送2號信號不會被遞達,pending對應位置修改為1
示例二:


先阻塞了2號信號,發送二號信號,pending修改,count==6時恢復了2號信號,2號信號遞達,執行捕捉代碼,最后信號執行完,對應pending修改為0
信號的處理
上面提到行程收到信號之后,不是立即處理信號,而是在合適的時候
這個合適的時候就是內核態切換回用戶態的時候
內核態通常執行OS代碼,權限優先級非常高
用戶態執行普通用戶的代碼的狀態,受OS的管理


自定義處理函式狀態切換4次
默認,忽略只切換了兩次
sigaction
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
作用:自定義捕捉信號
類似于sigprocmask
struct sigaction:

sa_sigaction:處理實時信號的介面
sa_handler:捕捉執行的方法(如果設定成SIG_DFL表示執行系統默認動作)
sa_flags:通常設定為0
sa_mask:說明需要額外屏蔽的信號
當某個信號的處理函式被呼叫時,內核自動將當前信號加入行程的信號屏蔽字(自動添加到mask中),當信號處理函式回傳時自動恢復原來的信號屏蔽字
示例:


volatile關鍵字
讓以下代碼在優化級別-O3 下運行


加上volatile后


就會捕獲信號退出回圈

加入volatile后,不會將flag先置入暫存器,而是在讀取記憶體中的值到暫存器中再檢測
volatile的作用:保證了記憶體可見性
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/356876.html
標籤:其他
