一、linux訊號是什麼
- 基本概念
訊號是事件發生時對程式的通知機制,也就是所謂的軟體中斷。訊號和硬體的中斷類似,是軟體層對中斷機制的模擬,在多數情況下是無法預測訊號產生的時間,所以軟體層提供了一種處理非同步事件的方法。
二、 訊號來源
訊號的來源分為硬體來源和軟體來源。
- 硬體來源:
- 硬體發生異常,即硬體檢測到錯誤條件並通知核心,隨即再由核心傳送相應的訊號給相關程式,如除數為0、無效的記憶體引用等。
- 使用者按終端鍵,引起終端產生的訊號(比如Ctrl + C鍵產生SIGINT)。
- 軟體來源:
- 使用者通過指令殺死,如kill指令。
- 發生軟體事件, 如程式執行raise, alarm、setitimer、sigqueue等函式。
三、 訊號處理
訊號通常是傳送給對應的程式,當訊號到達後,該程式需要做出相應的處理措施,通常程式會視具體訊號執行相應的操作,有三種操作方式。
- 忽略訊號:
訊號到達後、直接忽略,就好像是沒有出該訊號,訊號對該程式不會產生任何影響。事實上,大多數訊號都可以使用這種方式進行處理,但有兩種訊號卻決不能被忽略,分別是SIGKILL 和 SIGSTOP。 - 捕獲訊號:
當訊號到達程式後,執行signal()繫結好的訊號處理函式。 - 執行系統預設操作:
程式不對該訊號事件作出處理,而是交由系統進行處理,每一種訊號都會有其對應的系統預設的處理方式。
四、常見訊號
在linux系統中通過kill -l
命令可以檢視到相應的訊號。訊號編號是從 1 開始,不存在編號為 0 的訊號,事實上 kill()函式對訊號編號 0 有著特殊的應用。
注意:括號" ) "前面的數字對應該訊號的編號,編號 1~31 所對應的是不可靠訊號,編號 34~64 對應的是可靠訊號,從圖中可知,可靠訊號並沒有一個具體對應的名字,而是使用了 SIGRTMIN+N 或 SIGRTMAXN 的方式來表示。其中32和33空缺。
不可靠訊號表
值 | 名稱 | 解釋 | 預設動作 |
---|---|---|---|
1 | SIGHUP | 掛起 | |
2 | SIGINT | 中斷 | |
3 | SIGQUIT | 退出 | |
4 | SIGILL | 非法指令 | |
5 | SIGTRAP | 斷點或陷阱指令 | |
6 | SIGABRT | abort發出的訊號 | |
7 | SIGBUS | 非法記憶體訪問 | |
8 | SIGFPE | 浮點異常 | |
9 | SIGKILL | kill訊號 | 不能被忽略、處理和阻塞 |
10 | SIGUSR1 | 使用者訊號1 | |
11 | SIGSEGV | 無效記憶體訪問 | |
12 | SIGUSR2 | 使用者訊號2 | |
13 | SIGPIPE | 管道破損,沒有讀端的管道寫資料 | |
14 | SIGALRM | alarm發出的訊號 | |
15 | SIGTERM | 終止訊號 | |
16 | SIGSTKFLT | 棧溢位 | |
17 | SIGCHLD | 子程式退出 | 預設忽略 |
18 | SIGCONT | 程式繼續 | |
19 | SIGSTOP | 程式停止 | 不能被忽略、處理和阻塞 |
20 | SIGTSTP | 程式停止 | |
21 | SIGTTIN | 程式停止,後臺程式從終端讀資料時 | |
22 | SIGTTOU | 程式停止,後臺程式想終端寫資料時 | |
23 | SIGURG | I/O有緊急資料到達當前程式 | 預設忽略 |
24 | SIGXCPU | 程式的CPU時間片到期 | |
25 | SIGXFSZ | 檔案大小的超出上限 | |
26 | SIGVTALRM | 虛擬時鐘超時 | |
27 | SIGPROF | profile時鐘超時 | |
28 | SIGWINCH | 視窗大小改變 | 預設忽略 |
29 | SIGIO | I/O相關 | |
30 | SIGPWR | 關機 | 預設忽略 |
31 | SIGSYS | 系統呼叫異常 |
五、訊號處理
- signal()
"signal.h"訊號處理庫提供了signal函式,用來捕獲突發事件。以下是 signal() 函式的語法ads。
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
- signum:可使用訊號名(巨集)或訊號的數字編號,建議使用訊號名。
- handler:引數 handler 既可以設定為使用者自定義的函式,也可以設定為 SIG_IGN 或 SIG_DFL,SIG_IGN 表示此程式需要忽略該訊號,SIG_DFL 則表示設定為系統預設操作。
- raise()
有時程式需要向自身傳送訊號,raise()函式可用於實現這一要求.
int raise(int sig);
- sig:需要傳送的訊號。
- sigaction()
除了signal()之外,sigaction()系統呼叫是設定訊號處理方式的另一選擇,雖然 signal()函式簡單好用,而 sigaction()更為複雜,但作為回報,sigaction()也更具靈活性以及移植性。
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
- signum:需要設定的訊號,除了 SIGKILL 訊號和 SIGSTOP 訊號之外的任何訊號。
- act:引數 act 不為 NULL,則表示需要為訊號設定新的處理方式;如果引數 act 為 NULL,則表示無需改變訊號當前的處理方式
- oldact:引數oldact 不為 NULL,則會將訊號之前的處理方式等資訊通過引數 oldact 返回出來;如果無意獲取此類資訊,那麼可將該引數設定為 NULL。
- 返回值:成功返回 0;失敗將返回-1,並設定 errno。
struct sigaction 結構體
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
- sa_handler:指定訊號處理函式,與 signal()函式的 handler 引數相同。
- sa_sigaction:也用於指定訊號處理函式,這是一個替代的訊號處理函式。
- sa_mask:引數 sa_mask 定義了一組訊號。
- sa_restorer:該成員已過時,不要再使用了。
- sa_flags:引數 sa_flags 指定了一組標誌,這些標誌用於控制訊號的處理過程。
- kill()
kill()系統呼叫可將訊號傳送給指定的程式或程式組中的每一個程式。
int kill(pid_t pid, int sig);
- pid:引數 pid 為正數的情況下,用於指定接收此訊號的程式 pid。
- sig:引數 sig 指定需要傳送的訊號,也可設定為 0,如果引數 sig 設定為 0 則表示不傳送訊號,但任執行錯誤檢查,這通常可用於檢查引數 pid 指定的程式是否存在。
- 返回值:成功返回 0;失敗將返回-1,並設定 errno。
- alarm()
使用 alarm()函式可以設定一個定時器(鬧鐘),當定時器定時時間到時,核心會向程式傳送 SIGALRM訊號。
unsigned int alarm(unsigned int seconds);
- seconds:設定定時時間,以秒為單位;如果引數 seconds 等於 0,則表示取消之前設定的 alarm 鬧鐘。
- 返回值:如果在呼叫 alarm()時,之前已經為該程式設定了 alarm 鬧鐘還沒有超時,則該鬧鐘的剩餘值作為本次 alarm()函式呼叫的返回值,之前設定的鬧鐘則被新的替代;否則返回 0。
- pause()
pause()系統呼叫可以使得程式暫停執行、進入休眠狀態,直到程式捕獲到一個訊號為止,只有執行了訊號處理函式並從其返回時,pause()才返回,在這種情況下,pause()返回-1,並且將 errno 設定為EINTR。
int pause(void);
- 使用案例
demo1
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
void signal_handler(int signum)
{
printf("Interrupt signal (%d) received. \n", signum);
switch(signum){
case SIGINT:
printf("ctrl + c \n");
exit(signum);
break;
case SIGQUIT:
printf("ctrl + \\ \n");
exit(signum);
}
}
int main(int argc, char *argv[]) {
int i = 0;
//註冊訊號與訊號處理程式
signal(SIGINT, signal_handler);
signal(SIGQUIT, signal_handler);
while(1)
{
printf("Going to sleep....\n");
if (i > 5){
raise( SIGINT);
}
sleep(1);
i++;
}
return 0;
}
參考文獻
Linux訊號(signal)機制:http://gityuan.com/2015/12/20/signal/
linux 訊號及處理過程詳解:https://blog.csdn.net/u010765526/article/details/80085895
linux kill訊號詳解:https://www.cnblogs.com/gcb-1991/p/6922694.html
《正點原子應用程式設計指南》