一、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
《正點原子應用編程指南》