信號(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代替進程的原有掩碼,並暫停進程執行,直到收到信號再恢復原有掩碼並繼續執行進程。