Linux進程信號的發(fā)送和保存方法
一、信號發(fā)送
1、信號動作
通過指令man -7 signal查看信號的手冊,然后往下翻翻可以看到普通信號發(fā)出后對應的操作,以及它們的信號編號,和詳細描述信息

2、信號發(fā)送的本質
普通信號
信號發(fā)送的本質實際上是寫信號,把信號寫到進程PCB結構體對應的位圖上去,在進程的PCB中有這么一個位圖(是pending位圖,下面會說)正好對應著我們從1 ~ 31的普通信號編號,收到哪個信號就將哪一位對應的比特位置為1,表示收到信號,然后PCB再做對應的工作
值得注意的是,如果連續(xù)發(fā)普通信號,那么進程只會處理最后一次的信號,每次寫都是覆蓋寫的
實時信號
我們前面說過信號分為31個普通信號和31個實時信號,實時信號的作用類似于我們嵌入式RTOS實時運轉場景,要保持實時性,實時信號發(fā)送的本質類似于普通信號,不過此時我們保存信號的載體不再是一個位圖,而是一個結構體,它們被組織在信號隊列當中,誰先發(fā)送誰就先入隊,隊列遵循先入先出的規(guī)則,所以先發(fā)送也代表著先被處理
值得注意的是,如果連續(xù)發(fā)實時信號,那么進程會將隊列中的信號一個個全部處理
3、core dump
當程序在運行過程中發(fā)生崩潰(如段錯誤、除零錯誤等),Core dump 會記錄下程序崩潰瞬間的內存狀態(tài),包括寄存器的值、調用棧信息、全局變量和局部變量的值等,開發(fā)人員可以使用調試工具(如 GDB)加載 Core dump 文件,通過分析這些信息,準確地找到程序崩潰的位置和原因
我們可以通過ulimit -c 10240將core文件的大小限制修改為10240字節(jié),出現(xiàn)錯誤的時候core文件可能瞬間會被打滿的,所以我們云服務器上一般默認core文件的大小限制為0,我們要是用的話再修改它的大小限制即可
形成的文件叫做core.pid,pid就是出錯進程的pid,假設test進程出現(xiàn)錯誤,12314是它的pid,我們可以通過在gdb模式下輸入gdb test core.12314打印錯誤信息和原因
二、信號的保存
1、前置概念
實際執(zhí)行信號的處理動作稱為信號遞達
信號從產(chǎn)生到遞達之間的狀態(tài),稱為信號未決
被阻塞的信號產(chǎn)生是將保持在未決狀態(tài),直到進程解除對此信號的阻塞,才執(zhí)行遞達的動作

2、阻塞信號
信號被阻塞就是將信號阻塞在信號未決狀態(tài),具體實現(xiàn)阻塞功能的,是一個位圖block
block是一個位圖,共31位,對應1 ~ 31號信號,當對應比特位為1時,表示該編號信號被阻塞,為0則表示不阻塞
如果信號被阻塞則進入阻塞態(tài),若沒有被阻塞那么信號進入未決狀態(tài)
3、保存信號
未決狀態(tài)的作用就是保存信號,它保存信號的方式也是通過位圖pending
pending也是一個位圖,與block一致,對應的下標和信號編號也是一一對應,當對應比特位為1時,表示該編號信號處于未決狀態(tài),為0則信號遞達
實際上block和pending都屬于保存信號,只不過因為有兩個位圖,我們分開來說罷了
4、信號遞達
信號遞達后的信號,會執(zhí)行相對應的行為,有SIG_DFL:默認處理動作,SIG_IGN:忽略,和自定義處理sighandler,這在前面提到過

5、總結

一個信號,首先要經(jīng)過block,block為0來到pending,pending為0來到handler執(zhí)行動作,其中,9號和19號新號還是特例,它們是不能被阻塞和保存的,這兩個信號一旦發(fā)出就是直接handler,其實也不用handler了,它們對應的不可能為忽略和信號捕捉后的自定義函數(shù),只能為默認動作終止和暫停
三、信號集操作函數(shù)
信號集操作函數(shù)顧名思義就是操作信號集的函數(shù),sigset_t被稱作信號集,是操作系統(tǒng)提供的數(shù)據(jù)類型,用于描述位圖,下面就是信號集操作函數(shù)
#include <signal.h> int sigemptyset(sigset_t *set); // 將位圖全部設置為 0 int sigfillset(sigset_t *set); // 將位圖全部都設置為 1 int sigaddset (sigset_t *set, int signo); // 將位圖中的某一位設置為 1 int sigdelset(sigset_t *set, int signo); // 將位圖中的某一位設置為 0 int sigismember(const sigset_t *set, int signo); // 判斷一個信號是否在信號集中,不在返回0,在返回1,出錯返回-1
1、設置block位圖
sigprocmask是一個在信號處理中非常重要的系統(tǒng)調用,主要用于檢查、修改進程的信號掩碼(阻塞信號集)
#include <signal.h> int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
返回值:若成功則為0,若出錯則為-1
how:指定對信號掩碼的操作方式
how 取值 | 含義 | 示例說明 |
|---|---|---|
SIG_BLOCK | 將 set 所指向的信號集中的信號添加到當前的信號掩碼中,即阻塞 set 中的信號 | 若當前信號掩碼已阻塞 SIGINT,使用 SIG_BLOCK 并傳入包含 SIGTERM 的信號集,SIGTERM 也會被阻塞 |
SIG_UNBLOCK | 從當前的信號掩碼中移除 set 所指向的信號集中的信號,即解除對 set 中信號的阻塞 | 若當前信號掩碼阻塞了 SIGINT 和 SIGTERM,使用 SIG_UNBLOCK 并傳入包含 SIGINT 的信號集,SIGINT 信號的阻塞狀態(tài)將被解除 |
SIG_SETMASK | 將當前的信號掩碼設置為 set 所指向的信號集,覆蓋原來的信號掩碼 | 若原信號掩碼阻塞 SIGINT,使用 SIG_SETMASK 并傳入包含 SIGTERM 的信號集,信號掩碼將只阻塞 SIGTERM |
set:指向一個sigset_t類型的信號集,該信號集包含了要操作的信號,如果how的值為SIG_BLOCK或SIG_UNBLOCK,則set表示要添加或移除的信號集;如果how的值為SIG_SETMASK,則set表示要設置的新的信號掩碼,若該參數(shù)為NULL,則不改變當前的信號掩碼,僅獲取當前信號掩碼,此時oset不能為NULL
oset:指向一個sigset_t類型的信號集,用于存儲調用sigprocmask之前的信號掩碼,如果不需要保存舊的信號掩碼,可以將該參數(shù)設置為NULL
2、設置pending位圖
sigpending是一個用于獲取進程當前未決信號集的系統(tǒng)調用
#include <signal.h> int sigpending(sigset_t *set);
返回值:成功返回0,失敗返回-1
set:sigpending函數(shù)會將當前進程中處于未決狀態(tài)(即已發(fā)送但由于被阻塞而尚未被處理)的信號集存儲到set所指向的sigset_t對象中
3、設置handler行為
三種情況,默認,忽略和自定義,自定義那當然是signal函數(shù),前面有,不再贅述
四、驗證信號保存行為
#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
//打印出位圖
void PrintPending(const sigset_t &pset)
{
for(int i = 31; i >= 1; i--)
{
cout << sigismember(&pset, i);
}
cout << endl;
}
void handler(int signum)
{
cout << "catch a signum: " << signum << endl;
}
int main()
{
//自定義捕捉2號信號
signal(2, handler);
sigset_t bset, oset;
sigemptyset(&bset); // bset信號集清空
sigemptyset(&oset); // oset信號集清空
sigaddset(&bset, 2); // 將bset的第2位設為1,也就是給bset中添加上2號信號
// 調用系統(tǒng)調用,將數(shù)據(jù)設置進內核,設置block,此時2號信號被阻塞
sigprocmask(SIG_SETMASK, &bset, &oset);
// 重復打印當前進程的 pending 信號集,期間向進程發(fā)送 2號信號
// 因為 2號信號被阻塞了,所以 2號信號會一直被保存在 pending 中
sigset_t pset;
sigemptyset(&pset);// pset信號集清空
int cnt = 0;
//在15秒內,未接受信號前,都是一直打印0
while(true)
{
int n = sigpending(&pset);
if(n < 0) continue;
//打印位圖
PrintPending(pset);
sleep(1);
cnt++;
if(cnt == 15)
{
cout << "unblock 2 signo" << endl;
// 打印15次位圖后解除阻塞
sigdelset(&bset, 2);//將bset的第2位設置為0,也就是給bset去除2號新號,不阻塞2號
//設置當前信號屏蔽字為oset指向的值,也就是0
sigprocmask(SIG_SETMASK, &oset, nullptr);
}
}
return 0;
}
查看一下效果,在3秒后,我按下ctrl+c,然后我們的捕捉信號函數(shù)沒有工作,說明信號被阻塞了,然后15秒后我們自動放開阻塞,瞬間打印出handler函數(shù)定義要打印的信息,再按ctrl+c就正常進行handler行為了

到此這篇關于Linux進程信號的發(fā)送和保存方法的文章就介紹到這了,更多相關Linux進程信號發(fā)送和保存內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
apache time_wait連接數(shù)太多問題解決方法
這篇文章主要介紹了apache time_wait連接數(shù)太多問題解決方法,本文使用調整內核參數(shù)來解決,需要的朋友可以參考下2014-11-11
windows10 更新Ubuntu20.04 LTS的方法步驟
這篇文章主要介紹了windows10 更新Ubuntu20.04 LTS的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-06-06
Linux VPS配置Web網(wǎng)站環(huán)境一鍵包(LNMP/LAMP/LNMPA)
如果我們是資深Linux用戶,可能不屑于網(wǎng)上免費Linux Web一鍵包、管理面板的安裝,然后自己編譯或者自由的一套環(huán)境安裝配置環(huán)境。但是,對于大部分用戶而言,麥子個人建議還是選擇較為成熟的WEB一鍵包或者面板安裝環(huán)境2017-02-02
Xshell實現(xiàn)Windows上傳文件到Linux主機的方法
這篇文章主要介紹了Xshell實現(xiàn)Windows上傳文件到Linux主機的方法,需要的朋友可以參考下2017-12-12

