訊號(signal)是一種軟中斷,訊號機制是程序間通訊的一種方式,採用非同步通訊方式
一、訊號型別
Linux系統共定義了64種訊號,分為兩大類:可靠訊號與不可靠訊號,前32種訊號為不可靠訊號,後32種為可靠訊號。
1.1 概念
-
不可靠訊號: 也稱為非實時訊號,不支援排隊,訊號可能會丟失, 比如傳送多次相同的訊號, 程序只能收到一次. 訊號值取值區間為1~31;
-
可靠訊號: 也稱為實時訊號,支援排隊, 訊號不會丟失, 發多少次, 就可以收到多少次. 訊號值取值區間為32~64
1.2 訊號表
在終端,可透過kill -l
檢視所有的signal訊號
取值 | 名稱 | 解釋 | 預設動作 |
---|---|---|---|
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訊號,絕大部分的預設處理都是終止程序或停止程序,或dump核心映像轉儲。 上述的31的訊號為非實時訊號,其他的訊號32-64 都是實時訊號。
二、訊號產生
訊號來源分為硬體類和軟體類:
2.1 硬體方式
- 使用者輸入:比如在終端上按下組合鍵ctrl+C,產生SIGINT訊號;
- 硬體異常:CPU檢測到記憶體非法訪問等異常,通知核心生成相應訊號,併傳送給發生事件的程序;
2.2 軟體方式
透過系統呼叫,傳送signal訊號:kill(),raise(),sigqueue(),alarm(),setitimer(),abort()
- kernel,使用 kill_proc_info()等
- native,使用 kill() 或者raise()等
- java,使用 Procees.sendSignal()等
三、訊號註冊和登出
3.1 註冊
在程序task_struct結構體中有一個未決訊號的成員變數 struct sigpending pending
。每個訊號在程序中註冊都會把訊號值加入到程序的未決訊號集。
- 非實時訊號傳送給程序時,如果該資訊已經在程序中註冊過,不會再次註冊,故訊號會丟失;
- 實時訊號傳送給程序時,不管該訊號是否在程序中註冊過,都會再次註冊。故訊號不會丟失;
3.2 登出
- 非實時訊號:不可重複註冊,最多隻有一個sigqueue結構;當該結構被釋放後,把該訊號從程序未決訊號集中刪除,則訊號登出完畢;
- 實時訊號:可重複註冊,可能存在多個sigqueue結構;當該訊號的所有sigqueue處理完畢後,把該訊號從程序未決訊號集中刪除,則訊號登出完畢;
四、訊號處理
核心處理程序收到的signal是在當前程序的上下文,故程序必須是Running狀態。當程序喚醒或者排程後獲取CPU,則會從核心態轉到使用者態時檢測是否有signal等待處理,處理完,程序會把相應的未決訊號從連結串列中去掉。
4.1 處理時機
signal訊號處理時機: 核心態 -> signal訊號處理 -> 使用者態:
- 在核心態,signal訊號不起作用;
- 在使用者態,signal所有未被遮蔽的訊號都處理完畢;
- 當遮蔽訊號,取消遮蔽時,會在下一次核心轉使用者態的過程中執行;
4.2 處理方式
程序對訊號的處理方式: 有3種
- 預設 接收到訊號後按預設的行為處理該訊號。 這是多數應用採取的處理方式。
- 自定義 用自定義的訊號處理函式來執行特定的動作
- 忽略 接收到訊號後不做任何反應。
4.3 訊號安裝
程序處理某個訊號前,需要先在程序中安裝此訊號。安裝過程主要是建立訊號值和程序對相應資訊值的動作。
訊號安裝函式
- signal():不支援訊號傳遞資訊,主要用於非實時訊號安裝;
- sigaction():支援訊號傳遞資訊,可用於所有訊號安裝;
其中 sigaction結構體
- sa_handler:訊號處理函式
- sa_mask:指定訊號處理程式執行過程中需要阻塞的訊號;
- sa_flags:標示位
- SA_RESTART:使被訊號打斷的syscall重新發起。
- SA_NOCLDSTOP:使父程序在它的子程序暫停或繼續執行時不會收到 SIGCHLD 訊號。
- SA_NOCLDWAIT:使父程序在它的子程序退出時不會收到SIGCHLD訊號,這時子程序如果退出也不會成為僵 屍程序。
- SA_NODEFER:使對訊號的遮蔽無效,即在訊號處理函式執行期間仍能發出這個訊號。
- SA_RESETHAND:訊號處理之後重新設定為預設的處理方式。
- SA_SIGINFO:使用sa_sigaction成員而不是sa_handler作為訊號處理函式。
函式原型:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
- signum:要操作的signal訊號。
- act:設定對signal訊號的新處理方式。
- oldact:原來對訊號的處理方式。
- 返回值:0 表示成功,-1 表示有錯誤發生。
4.4 訊號傳送
- kill():用於向程序或程序組傳送訊號;
- sigqueue():只能向一個程序傳送訊號,不能像程序組傳送訊號;主要針對實時訊號提出,與sigaction()組合使用,當然也支援非實時訊號的傳送;
- alarm():用於呼叫程序指定時間後發出SIGALARM訊號;
- setitimer():設定定時器,計時達到後給程序傳送SIGALRM訊號,功能比alarm更強大;
- abort():向程序傳送SIGABORT訊號,預設程序會異常退出。
- raise():用於向程序自身傳送訊號;
4.5 訊號相關函式
訊號集操作函式
- sigemptyset(sigset_t *set):訊號集全部清0;
- sigfillset(sigset_t *set): 訊號集全部置1,則訊號集包含linux支援的64種訊號;
- sigaddset(sigset_t *set, int signum):向訊號集中加入signum訊號;
- sigdelset(sigset_t *set, int signum):向訊號集中刪除signum訊號;
- sigismember(const sigset_t *set, int signum):判定訊號signum是否存在訊號集中。
訊號阻塞函式
- sigprocmask(int how, const sigset_t *set, sigset_t *oldset)); 不同how引數,實現不同功能
- SIG_BLOCK:將set指向訊號集中的訊號,新增到程序阻塞訊號集;
- SIG_UNBLOCK:將set指向訊號集中的訊號,從程序阻塞訊號集刪除;
- SIG_SETMASK:將set指向訊號集中的訊號,設定成程序阻塞訊號集;
- sigpending(sigset_t *set)):獲取已傳送到程序,卻被阻塞的所有訊號;
- sigsuspend(const sigset_t *mask)):用mask代替程序的原有掩碼,並暫停程序執行,直到收到訊號再恢復原有掩碼並繼續執行程序。
轉:https://gityuan.com/2015/12/20/signal/