Linux中訊號
概述
》 訊號是軟體中斷,它是在軟體層次上對中斷機制的一種模擬。
訊號可以導致一個正在執行的程序被另一個正在執行的非同步程序中斷,轉而處理某一個突發事件。
》 訊號是一種非同步通訊方式。程序不必等待訊號的到達,程序也不知道訊號什麼時候到達。
》 訊號可以直接進行使用者空間程序和核心空間程序的互動,核心程序可以利用它來通知使用者空間程序發生了哪些系統事件。
》 每個訊號的名字都以字元SIG開頭。
》 每個訊號和一個數字編碼相對應,在標頭檔案signum.h中,這些訊號都被定義為正整數。
》 訊號名定義路徑:/usr/include/i386-Linux-gnu/bits/signum.h
》 在Linux下,要想檢視這些訊號和編碼的對應關係,可使用命令:kill -l
1、當用戶按某些終端鍵時,將產生訊號。
例如:
終端上按“Ctrl+c”組合鍵通常產生中斷訊號SIGINT、終端上按"Ctrl+"鍵通常產生中斷訊號SIGQUIT、終端上按"Ctrl+z"鍵通常產生中斷訊號SIGSTOP。
2、硬體異常將產生訊號。
除數為0,無效的記憶體訪問等。這些情況通常由硬體檢測到,並通知核心,然後核心產生適當的訊號傳送給相應的程序。
3、軟體異常將產生訊號。
當檢測到某種軟體條件已發生,並將其通知有關程序時,產生訊號。
4、呼叫kill函式將傳送訊號。
注意:接收訊號程序和傳送訊號程序的所有者必須相同,或傳送訊號程序的所有者必須是超級使用者。
5、執行kill命令將傳送訊號。
此程式實際上是使用kill函式來發送訊號。也常用此命令終止一個失控的後臺程序。
》 一個程序收到一個訊號的時候,可以用如下方法進行處理:
1.執行系統預設動作
對大多數訊號來說,系統預設動作是用來終止該程序。
2.忽略此訊號
接收到此訊號後沒有任何動作。
3.執行自定義訊號處理函式
用使用者定義的訊號處理函式處理該訊號。
注意:
SIGKILL和SIGSTOP不能更改訊號的處理方式,因為它們向用戶提供了一種使程序終止的可靠方法 。
訊號的基本操作
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int signum);
功能:
給指定程序傳送訊號。
引數:
pid:詳見下面
signum:訊號的編號
返回值:
成功返回 0
失敗返回 -1
pid的取值有4種情況:
pid>0: 將訊號傳送給程序ID為pid的程序。
pid=0: 將訊號傳送給當前程序所在程序組中的所有程序。
pid=-1: 將訊號傳送給系統內所有的程序。
pid<-1: 將訊號傳給指定程序組的所有程序。這個程序組號等於pid的絕對值。
例:01_kill.c
注意:
使用kill函式傳送訊號,接收訊號程序和傳送訊號程序的所有者必須相同,或者傳送訊號程序的所有者是超級使用者。
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
功能:
在seconds秒後,向呼叫程序傳送一個SIGALRM訊號,SIGALRM訊號的預設動作是
終止呼叫alarm函式的程序。
返回值:
若以前沒有設定過定時器,或設定的定時器已超時,返回0;否則返回定時器剩餘的秒數,
並重新設定定時器。
例:02_alarm.c
#include <signal.h> int raise(int signum); 功能: 給呼叫程序本身送一個訊號。 引數: signum:訊號的編號。 返回值: 成功返回 0 失敗返回 -1
例:03_raise.c
#include <stdlib.h>
void abort(void);
功能:
向程序傳送一個SIGABRT訊號,預設情況下程序會退出。
注意:
即使SIGABRT訊號被加入阻塞集,一旦程序呼叫了abort函式,程序也還是會被終止,且在終止前會重新整理緩衝區,關檔案描述符。
#include <unistd.h>
int pause(void);
功能:
將呼叫程序掛起直至捕捉到訊號為止。這個函式通常用於判斷訊號是否已到。
返回值:
直到捕獲到訊號,pause函式才返回-1,且errno被設定成EINTR。
例:04_pause.c
程序接收到訊號後有如下三種處理方式:
1、執行系統預設動作
2、忽略此訊號
3、執行自定義訊號處理函式
程式中可用函式signal()改變訊號的處理方式
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_thandler);
功能:
註冊訊號處理函式(不可用於SIGKILL、SIGSTOP訊號),即確定收到訊號後處理函式的入口地址。
引數:
signum:訊號編號
handler的取值:
忽略該訊號:SIG_IGN
執行系統預設動作:SIG_DFL
自定義訊號處理函式:訊號處理函式名
返回值:
成功:返回函式地址,該地址為此訊號上一次註冊的訊號處理函式的地址。
失敗:返回SIG_ERR
例 05_signal.c
可重入函式
可重入函式是指函式可以由多個任務併發使用,而不必擔心資料錯誤。
編寫可重入函式:
》不使用(返回)靜態的資料、全域性變數(除非用訊號量互斥)。
》不呼叫動態記憶體分配、釋放的函式。
》不呼叫任何不可重入的函式(如標準I/O函式)。
注:即使訊號處理函式使用的都是可重入函式(常見的可重入函式),也要注意進入處理函式時,首先要儲存errno的值,結束時,再恢復原值。因為,訊號處理過程中,errno值隨時可能被改變。
》 訊號集
訊號集概述:一個使用者程序常常需要對多個訊號做出處理。為了方便對多個訊號進行處理,在Linux系統中引入了訊號集。
》 訊號集是用來表示多個訊號的資料型別。
》 訊號集資料型別sigset_t
》 定義路徑:/usr/include/i386-Linux-gnu/bits/sigset.h
訊號集相關的操作主要有如下幾個函式:
sigemptyset
sigfillset
sigismember
sigaddset
sigdelset
#include <signal.h>
int sigemptyset(sigset_t *set);
功能:
初始化由set指向的訊號集,清除其中所有的訊號即初始化一個空訊號集。
引數:
set:訊號集標識的地址,以後操作此訊號集,對set進行操作就可以了。
返回值:
成功返回 0,失敗返回 -1。
#include <signal.h>
int sigfillset(sigset_t *set);
功能:
初始化訊號集合set, 將訊號集合設定為所有訊號的集合。
引數:
訊號集標識的地址,以後操作此訊號集,對set進行操作就可以了。
返回值:
成功返回 0,失敗返回 -1。
#include <signal.h>
int sigismember(const sigset_t *set,int signum);
功能:
查詢signum標識的訊號是否在訊號集合set之中。
引數:
set:訊號集識別符號號的地址。
signum:訊號的編號。
返回值:
在訊號集中返回 1,不在訊號集中返回 0
錯誤,返回 -1
#include <signal.h>
int sigaddset(sigset_t *set, int signum);
功能:
將訊號signum加入到訊號集合set之中。
引數:
set:訊號集標識的地址。
signum:訊號的編號。
返回值:
成功返回 0,失敗返回 -1。
#include <signal.h>
int sigdelset(sigset_t *set, int signum);
功能:
將signum所標識的訊號從訊號集合set中刪除。
引數:
set:訊號集標識的地址。
signum:訊號的編號。
返回值:
成功:返回 0
失敗:返回 -1
例:06_signal_set.c
》 訊號阻塞集(遮蔽集、掩碼)
每個程序都有一個阻塞集,它用來描述哪些訊號遞送到該程序的時候被阻塞(在訊號發生時記住它,直到程序準備好時再將訊號通知程序)。
所謂阻塞並不是禁止傳送訊號, 而是暫緩訊號的傳送。若將被阻塞的訊號從訊號阻塞集中刪除,且對應的訊號在被阻塞時發生了,程序將會收到相應的訊號。
#include <signal.h>
int sigprocmask(int how,
const sigset_t *set, sigset_t *oldset);
功能:
檢查或修改訊號阻塞集,根據how指定的方法對程序的阻塞集合進行修改,新的訊號阻塞集由set指定,
而原先的訊號阻塞集合由oldset儲存。
引數:
how:訊號阻塞集合的修改方法。
set:要操作的訊號集地址。
oldset:儲存原先訊號集地址。
返回值:
成功:返回 0
失敗:返回 -1
》 how:
SIG_BLOCK:向訊號阻塞集合中新增set訊號集
SIG_UNBLOCK:從訊號阻塞集合中刪除set集合
SIG_SETMASK:將訊號阻塞集合設為set集合
注:若set為NULL,則不改變訊號阻塞集合,函式只把當前訊號阻塞集合儲存到oldset中。
例:06_sigprocmask.c