kill -l
可以查看所有信號(hào):
其中,前面的數(shù)字就是信號(hào),后面的大寫英文就是信號(hào)名稱,實(shí)際就是宏。
我們需要關(guān)注的是 1~31 號(hào)普通信號(hào),關(guān)注他們有沒有產(chǎn)生(可以用 0 或者 1 表示)。
所以,進(jìn)程的 pcb 中,需要對(duì)產(chǎn)生的信號(hào)先用 位圖 保存起來(lái),再按照一定的順序去處理他們。
我們所謂發(fā)送信號(hào),本質(zhì)其實(shí)就是寫入信號(hào),直接修改特定進(jìn)程的信號(hào)位圖中的特定比特位(0 / 1)。位圖中,比特位的位置,是信號(hào)的編號(hào);比特位的內(nèi)容表示,是否收到該信號(hào)。
無(wú)論后面有多少種信號(hào)產(chǎn)生的方式,最終都必須要讓 OS 來(lái)完成最后發(fā)送 / 寫入的過(guò)程。
man 7 signal
:查看所有信號(hào)詳細(xì)信息
一、 signal 函數(shù):用戶自定義捕捉信號(hào)
為滿足義務(wù)要求,用戶可以對(duì)特殊信號(hào)進(jìn)行捕捉,如下 signal
函數(shù)就可以對(duì)信號(hào)捕捉,設(shè)置處理動(dòng)作,接收到設(shè)定信號(hào)的時(shí)候就會(huì)觸發(fā)。
#include <signal.h>
typedef void (*sighandler_t)(int);
?sighandler_t signal(int signum, sighandler_t handler);
?
參數(shù) signum:
- 信號(hào)編號(hào)
?
參數(shù) handler:
- 用戶自定義處理動(dòng)作,在 signum 信號(hào)發(fā)生時(shí)觸發(fā)。
??使用舉例:
// 自定義方法
// signo:特定信號(hào)被發(fā)送給當(dāng)前進(jìn)程,執(zhí)行handler方法的時(shí)候,要自動(dòng)填充對(duì)應(yīng)的信號(hào)給handler方法
// 我們甚至可以給所有的信號(hào)設(shè)置同一個(gè)處理函數(shù)
void handler(int signo)
{
std::cout << "get a singal: " << signo << std::endl;
// exit(2);
}
int main()
{
// signal(SIGINT, handler);
// signal(SIGQUIT, handler);
signal(2, handler); // 2:ctrl+c
signal(3, handler); // 3:ctrl+\
while(true)
{
std::cout << "我是一個(gè)進(jìn)程,我正在運(yùn)行 ..., pid: " << getpid() << std::endl;
sleep(1);
}
return 0;
}
1. 2 號(hào)信號(hào),進(jìn)程的默認(rèn)處理動(dòng)作是終止進(jìn)程
2. signal 可以進(jìn)行對(duì)指定的信號(hào)設(shè)定自定義處理動(dòng)作
3. signal(2, handler)調(diào)用完這個(gè)函數(shù)的時(shí)候,handler 方法沒有被調(diào)用,只是更改了 2 號(hào)信號(hào)的處理動(dòng)作。
4. 那么 handler 方法在當(dāng) 2 號(hào)信號(hào)產(chǎn)生的時(shí)候才會(huì)被調(diào)用
5. 原本 2 號(hào)信號(hào)的默認(rèn)處理動(dòng)作是 終止進(jìn)程,我們定義 signal(2, handler) 后,執(zhí)行的就變成用戶動(dòng)作的自定義捕捉!
6. 不是每個(gè)信號(hào)我們都可以自定義捕捉的??!比如 9 就不行。是 OS 規(guī)定的。(man 7 signal:查看所有信號(hào)詳細(xì)信息)
二、信號(hào)的產(chǎn)生
1. 通過(guò)中斷按鍵產(chǎn)生信號(hào)
比如用戶按下 ctrl+c,鍵盤輸入產(chǎn)生一個(gè) 硬件中斷,用電信號(hào)將中斷號(hào)寫入寄存器,系統(tǒng)再根據(jù)中斷號(hào)去中斷向量表中查找,然后 OS 再?gòu)逆I盤中去讀取數(shù)據(jù)(看是鍵盤哪些位置被摁下)。被 OS 獲取后,解釋成信號(hào),發(fā)送給目標(biāo)前臺(tái)進(jìn)程。
2. 調(diào)用系統(tǒng)函數(shù)向進(jìn)程發(fā)信號(hào)
2.1 kill 函數(shù):給任意進(jìn)程發(fā)送任意信號(hào)
頭文件
#include <signal.h>
int kill(pid_t pid, int sig);
?
參數(shù) pid:
- 進(jìn)程pid
?參數(shù) sig:
- 信號(hào)
2.2 raise 函數(shù):給調(diào)用進(jìn)程發(fā)送任意信號(hào)
頭文件
#include <signal.h>
int raise(int sig);
?
參數(shù) sig:
- 信號(hào)
2.3 abort 函數(shù):給調(diào)用進(jìn)程發(fā)送 6 號(hào)信號(hào)
頭文件
#include <stdlib.h>
void abort(void);
進(jìn)程收到 6 號(hào)信號(hào)就會(huì)終止,即使可以被用戶捕捉到,也會(huì)完成終止。
3. 軟件條件產(chǎn)生信號(hào)
(SIGPIPE 也是一個(gè)由軟件條件產(chǎn)生的信號(hào)
alarm 函數(shù):鬧鐘時(shí)間后,發(fā)送 14(SIGALRM )號(hào)信號(hào)
默認(rèn)動(dòng)作是終止(Term)當(dāng)前進(jìn)程
頭文件
#include <unistd.h>
unsigned alarm(unsigned seconds);
?
參數(shù) seconds:
- 時(shí)間
?
返回值:
- 0,或者是以前設(shè)定的鬧鐘時(shí)間還余下的秒數(shù)。(比如第一次提前結(jié)束,在次重新設(shè)定時(shí),就會(huì)返回之前剩余的時(shí)間)
alarm 函數(shù)是一次性的,可以利用捕捉器,進(jìn)行 自取 操作,達(dá)到不斷設(shè)置鬧鐘的作用。
??使用舉例:
void myhandler(int signo)
{
std::cout << "get a signal: " << signo << " count: " << count << std::endl;
alarm(1);
// exit(0);
}
int main(int argc, char *argv[])
{
std::cout << "pid: " << getpid() << std::endl;
signal(SIGALRM, myhandler);
alarm(1); //一次性的
while(true)
{
sleep(1);
}
}
alarm 也是有內(nèi)核數(shù)據(jù)結(jié)構(gòu)的,OS 管理這些內(nèi)核數(shù)據(jù)結(jié)構(gòu),每隔一段時(shí)間就會(huì)去比如說(shuō)管理 alarm 的最小堆中,當(dāng)堆頂 timestamp >= 系統(tǒng)當(dāng)前時(shí)間 時(shí),就會(huì)給這個(gè)對(duì)應(yīng)的進(jìn)程 pid 發(fā)送 SIGALRM 信號(hào),并把這個(gè)鬧鐘從堆中拿走。
struct alarm
{
int timestamp; // curr + 設(shè)置的seconds
// ...進(jìn)程 pid ...等等
};
4. 硬件異常產(chǎn)生信號(hào)
硬件異常被硬件以某種方式被硬件檢測(cè)到并通知內(nèi)核,然后內(nèi)核向當(dāng)前進(jìn)程發(fā)送適當(dāng)?shù)男盘?hào)。
4.1 除0:8) SIGFPE
Signal | Value | Action | comment |
---|---|---|---|
SIGFPE | 8 | Core | Floating point exception |
例如當(dāng)前進(jìn)程執(zhí)行了除 0 的指令,CPU的運(yùn)算單元會(huì)產(chǎn)生異常,內(nèi)核將這個(gè)異常解釋為 SIGFPE 信號(hào),發(fā)送給進(jìn)程。
狀態(tài)寄存器:用比特位表示狀態(tài),其中有一位,就是反映本次計(jì)算是否有溢出問題。
出現(xiàn)除 0 后,溢出標(biāo)志位被置 1,os 發(fā)現(xiàn)后立即將 相應(yīng)進(jìn)程 pcb 中發(fā)送 8號(hào) 信號(hào)。
4.2 野指針:11) SIGSEGV
Signal | Value | Action | comment |
---|---|---|---|
SIGSEGV | 11 | Core | Invalid memory reference |
再比如當(dāng)前進(jìn)程訪問了非法內(nèi)存地址,MMU 會(huì)產(chǎn)生異常,內(nèi)核將這個(gè)異常解釋為 SIGSEGV 信號(hào)發(fā)送給進(jìn)程。
虛擬地址 通過(guò)頁(yè)表 轉(zhuǎn)換訪問到物理內(nèi)存,這個(gè)過(guò)程其實(shí)是軟硬件結(jié)合的方式完成的。這個(gè)頁(yè)表的 KV 轉(zhuǎn)換過(guò)程就是由硬件 MMU 完成的。
MMU:內(nèi)存管理單元,被集成在 CPU 內(nèi),轉(zhuǎn)換時(shí),只需要把虛擬地址導(dǎo)入到 MMU 這個(gè)硬件中,用這個(gè)硬件轉(zhuǎn)。
??舉例:
// 一個(gè)野指針問題
int *p = nullptr;
p* = 100;
分析上述代碼,當(dāng)我們賦值給指針為 nullptr 時(shí),p 里面放的是 0 或者 nullptr,*p 代表的就是虛擬地址空間中的 0 號(hào)地址,我們想將 100 寫入 0 號(hào)地址,但這個(gè)地址我們沒有申請(qǐng)過(guò),他也不允許我們?cè)L問,所以造成了野指針 / 懸垂指針問題。
其實(shí),*p = 100;第一步并不是寫入,而是首先進(jìn)行虛擬地址到物理內(nèi)存的轉(zhuǎn)換。
- 沒有映射:MMU 硬件報(bào)錯(cuò)
- 有映射,但是沒有權(quán)限:MMU 直接報(bào)錯(cuò)
- OS 接收到報(bào)錯(cuò)后,傳遞給 CPU 中的一個(gè)寄存器,找到相應(yīng)進(jìn)程的 pcb 對(duì)其發(fā)送 11號(hào) 信號(hào)。
三、信號(hào)保存的細(xì)節(jié)
1. core 和 term
查看信號(hào)的 Action 欄有 core 和 term 兩種。
他們有什么不同呢?
-
term 終止的就是終止,沒有多余的動(dòng)作。
-
core 終止,會(huì)先進(jìn)行核心轉(zhuǎn)儲(chǔ),再進(jìn)行終止。
進(jìn)程在異常的時(shí)候,OS 可以將該核心代碼部分進(jìn)行 核心轉(zhuǎn)儲(chǔ),將內(nèi)存中進(jìn)程的相關(guān)數(shù)據(jù),全部 dump 到磁盤中。核心轉(zhuǎn)儲(chǔ)的目的是,方便異常后進(jìn)行調(diào)試。
一般核心轉(zhuǎn)儲(chǔ)文件在云服務(wù)器上確實(shí)看不到,而是云服務(wù)器默認(rèn)是關(guān)閉這個(gè)功能的!
ulimit -a
可以查到 core_file_size 大小是0,即關(guān)閉的,按照提示ulimit -c [數(shù)值]
設(shè)置大小,即可打開核心轉(zhuǎn)儲(chǔ)功能,數(shù)值設(shè)為 0 就是關(guān)閉。
當(dāng)程序異常時(shí),我們不知道哪里出了問題。有如下解決方法,稱作 事后調(diào)試:
-
-g
生成可執(zhí)行程序,使用gdb
命令打開調(diào)試模式 -
命令行中輸入:
core-file core.xxxx
(xxxx 為相應(yīng)的核心轉(zhuǎn)儲(chǔ)生成文件),就會(huì)出現(xiàn)報(bào)錯(cuò)原因的詳細(xì)信息,和報(bào)錯(cuò)位置
既然核心轉(zhuǎn)儲(chǔ)那么方便,為什么云服務(wù)器要關(guān)閉這個(gè)功能呢?
Linux環(huán)境根據(jù)使用目的可以分為:開發(fā)環(huán)境、測(cè)試環(huán)境、生產(chǎn)環(huán)境
云服務(wù)器屬于生產(chǎn)環(huán)境,生產(chǎn)環(huán)境是默認(rèn)關(guān)閉核心轉(zhuǎn)儲(chǔ)的!
按照 CPU 的運(yùn)行速度,錯(cuò)誤的代碼,在短時(shí)間內(nèi)可以造成大量的 core. 文件,磁盤寫滿甚至系統(tǒng)崩潰都是有可能的,所以生產(chǎn)環(huán)境下,一般都是將這個(gè)功能關(guān)閉的。
2. waitpid 中,status 第八位的 core dump 標(biāo)志位
??關(guān)于 waitpid 詳細(xì)內(nèi)容點(diǎn)擊跳轉(zhuǎn)
這里的 core dump 標(biāo)志位,就是用來(lái)記錄,是否有 core dump 出現(xiàn)的。
?? 接下篇
????進(jìn)程信號(hào)篇Ⅱ:信號(hào)的阻塞及保存(sigset_t, sigprocmask, sigpending)、信號(hào)的處理、信號(hào)的捕捉(sigaction)
????進(jìn)程信號(hào)篇Ⅲ:可重入函數(shù)、volatile關(guān)鍵字、SIGCHLD信號(hào)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-824170.html
??如果本文對(duì)你有些幫助,請(qǐng)給個(gè)贊或收藏,你的支持是對(duì)作者大大莫大的鼓勵(lì)??!(????) 歡迎評(píng)論留言~~文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-824170.html
到了這里,關(guān)于【Linux】進(jìn)程信號(hào)篇Ⅰ:信號(hào)的產(chǎn)生(signal、kill、raise、abort、alarm)、信號(hào)的保存(core dump)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!