訊號集與阻塞訊號
阿新 • • 發佈:2019-01-11
linux常見的訊號
訊號是一種軟體中斷,是一種處理程序間非同步的通訊機制。訊號可以導致一個正在執行的程序被另一個非同步程序中斷,轉而處理某一個突發事件。
常見的訊號
kill -l 命令檢視
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18 ) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS
通常對於一個程序傳送訊號時
- 對於一個程序即便沒有收到訊號,它也直到怎樣去檢測處理它。
- 作業系統向一個程序傳送訊號,實際上去修改了目標檔案pcb中資訊欄位。
- 當一個程序到訊號時並不會立即去處理,而是在合適的時候(從核心態切換到使用者態)。
- 程序檢測自己收到訊號是因為自己pcb中資訊欄位設定未有效(從0置為1)
訊號的產生
- 使用者在終端按下某些組合鍵(會被作業系統解釋為訊號),終端驅動程式會發送訊號給前臺程式。
//用一個簡單的死迴圈驗證,當按下組合鍵ctrl-c時,就是向該程序傳送了一個訊號,終止該程序。
#include<stdio.h>
int main()
{
while(1)
{
printf("pid is %d\n",getpid());
sleep(1);
turn 0;
}
- 硬體異常產生訊號。例如程序執行了除0的指令,cpu的運算單元會產生異常,核心將這個異常解釋為SIGFPE訊號傳送給程序。還有執行了一個無法儲存訪問的程序產生一個SIGSEGV
#include<stdio.h>
int main()
{
int a=10;
int b=a/0;
printf("b=%d\n",b);
return 0;
}
//進行了除0的操作,硬體異常產生訊號SIGFPE,程序異常退出
程序執行後異常退出,並顯示core dumped。
core dumped:表示一個程序異常退出,作業系統會將異常資訊轉儲到硬碟上,是便於gdb除錯。
- 終止程序訊號。程序呼叫kill函式可以發生訊號給另一個程序,也可以kill命令傳送訊號給某個程序。
//任意程序之間傳送任意訊號
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
//pid是傳遞訊號的程序號,sig為傳送的訊號值
//一個程序給自己傳送任意訊號
#include <signal.h>
int raise(int sig)
//一個程序給自己傳送唯一訊號
#include <stdlib.h>
void abort(void);
- 軟體異常產生訊號。當檢測到某種軟體條件已經發生,並將其通知有關程序時也會產生訊號。
//特定時間後給程序傳送訊號(SIGALRM),該訊號的預設動作是終止該程序
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
//函式的返回值是0或設定的鬧鐘剩餘的秒數
訊號的處理
- 忽略此訊號。
- 執行該訊號的預設處理動作
- 自定義捕捉。提供一個訊號處理函式,要求核心在處理訊號時切換到使用者態執行這個處理函式。
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
//訊號處理函式,第一個引數為接收到的訊號,第二個引數函式指標使接收到訊號後處理程式碼入口。
阻塞訊號
- 訊號遞達(Delivery):實際執行訊號的處理動作。
- 訊號未決(pending):訊號從產生到遞達之間的狀態。
- 訊號阻塞(block):產生訊號,訊號也不會被遞達。被阻塞的訊號產生時將保持在未決狀態,訊號被阻塞了就不被遞達。
未決和阻塞標誌可以用相同的資料型別sigset_t儲存,sigset_t稱為訊號集
訊號在核心中的示意圖
訊號產生時,核心在該程序控制塊中設定該訊號的未決標誌,直到訊號遞達才清除該標誌。
特點 - 訊號未阻塞也未產生,當它遞達時執行預設處理動作
- 訊號產生但被阻塞,所以暫時不能遞達。儘管處理動作時忽略,但是在解除阻塞之前還不能忽略該訊號,因為程序仍有機會改變對訊號的處理動作之後再解除阻塞。
訊號集操作函式
#include <signal.h>
int sigemptyset(sigset_t *set);
//對set指向的訊號集進行初始化,使所有訊號的清空,即沒有任何訊號再集合中
int sigfillset(sigset_t *set);
//用來填空填空訊號集,即阻塞所有訊號
int sigaddset(sigset_t *set, int signum);
//將某個訊號新增到訊號集,第一個引數為新增的訊號集,第二個為新增的訊號
int sigdelset(sigset_t *set, int signum);
//從訊號集中刪除某個訊號
int sigismember(const sigset_t *set, int signum);
//檢測訊號集是否為空
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
//讀取或改變程序的訊號遮蔽字。成功返回0,失敗則返回-1.
如果oldset非空,則讀取程序當前的訊號遮蔽字通過oldset傳出。如果set非空,則更改程序的訊號遮蔽字,引數how表示如何更改。如果set和oldset都非空,則先將原來的遮蔽字備份到oldset,再根據set和how引數更改遮蔽字。
how含義
- SIG_BLOCK:將第二個引數所描述的集合新增到當前程序阻塞的訊號中
- SIG_UNBLOCK:將第二個引數鎖描述的集合從當前的程序阻塞的訊號集中刪除
- SIG_SETMASK:無論之前的阻塞號。設定當前程序阻塞的集合的第二個引數描述的物件
#include <signal.h>
int sigpending(sigset_t *set);
//讀取當前程序的未決訊號集,通過set引數傳出。成功返回0,失敗返回-1
#include <signal.h>
int sigpending(sigset_t *set);
//讀取當前的未決訊號,通過set傳出
程式碼實現:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void printsigset(sigset_t *set)
{
int i=0;
for(i=1;i<=31;i++)
{
if(sigismember(set,i))//檢測該訊號是否存在與
{
printf("1");
}
else
{
printf("0");
}
}
printf("\n");
}
void handler(int sig)
{
printf("get a sig%d\n",sig);
}
int main()
{
sigset_t blockset,oblockset,pending;
//定義三個訊號集,其中pending是用來獲取未決訊號集的
sigemptyset(&blockset);
sigemptyset(&oblockset);//將blocket,oblocket初始化清空置零
sigaddset(&blockset,2);//將2號訊號新增到blocket訊號集中
signal(2,handler);
sigprocmask(SIG_SETMASK,&blockset,&oblockset);//將2號訊號block阻塞,將原來的值讀到oblockset
int count=0;
while(1)
{
sigpending(&pending);//獲取當前的未決訊號
printsigset(&pending);//並顯示出來
sleep(1);
if(++count==10)
{
//printf("recover proc block set\n",count)
sigprocmask(SIG_SETMASK,&oblockset,NULL);//當count加到10時,對pending表進行操作,當前的遮蔽字為oblockset對應的訊號集,也就是清除對2號訊號的阻塞,也就還原回去。
}
}
}