通過本文你會了解到:
1. 信號機制簡介
2. 信號處理函數( signal/sigaction/kill)
信號機制簡介
信號機制就是進程間相互傳遞消息的機制,又稱做軟中斷信號。就像電話一樣,你不能確定什麼時候有人打電話,隨時隨地發生。linux系統中處理信號有一下幾種方式:
- Term - 終止進程
- Ign - 忽略該信號
- Core - 終止該進程並保存內存信息
- Stop - 停止進程
- Cont - 恢復已停止的進程
除了上述的5種處理,用戶還可以自定義處理函數。
信號處理函數
那麼在linux中應用什麼函數來實現對信號進行處理呢,接下來我們一起來學習幾個常用函數:
signal函數 – 爲信號設置處理方法 – 早期linux版本使用,知道怎麼用就可以了,在編程時還是不要用這麼老的函數了。
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signum
- 信號值
handler
- 可以爲自定義的信號處理函數,也可以是SIG_IGN
(忽略該信號)和SIG_DEL
(信號默認處理)。
下面以大家最熟悉的ctrl-c(終止進程)爲例,根據handler取值不同給出實例(終於可以上代碼了,竊喜):
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int signo)
{
switch(signo) {
case SIGINT:
printf("handle SIGINT by user\n");
break;
default:
break;
}
}
int main(int argc, char **argv)
{
printf("ctr-c will be handled by user, enter to continue..\n");
signal(SIGINT, signal_handler);
getchar();
printf("ctr-c will be ignore, enter to continue..\n");
signal(SIGINT, SIG_IGN);
getchar();
printf("ctr-c will handle by default, enter to continue..\n");
signal(SIGINT, SIG_DFL);
getchar();
printf("thanks, bye!\n");
return 0;
}
運行測試:
$ ./signal
ctr-c will be handled by user, enter to continue..
^Chandle SIGINT by user // 信號被自定義函數獲取
^Chandle SIGINT by user // 信號被自定義函數獲取
ctr-c will be ignore, enter to continue..
^C^C // 信號被忽略
ctr-c will handle by default, enter to continue..
^C // 默認操作,終止進程
sigaction函數 - 爲信號設置處理方法
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
signum
- 信號值
act
- sigaction結構體,指定當前信號的處理信息
oldact
- sigaction結構體,返回信號之前的處理信息
要了解後兩個參數的作用必須要瞭解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
函數中的處理函數作用相同,可以爲用戶自定義的處理函數,也可以爲SIG_IGN
和SIG_DFL
sa_sigaction
- 信號的另一個處理函數,當sa_flags
含有SA_SIGINFO時,應用此處理函數。此函數可以提供信號的更過信息。
sa_mask
- 指定在信號處理函數運行期間需要屏蔽的信號。屏蔽的信號會被阻塞直到處理函數結束。
sa_flags
- 指定信號的處理行爲,可以爲0
或SA_NOCLDSTOP
/A_NOCLDSTOP
等,具體參考man 7 signal
sa_restorer
- 暫爲使用,保留。
sigaction的使用也時比較複雜的,後續會結合具體實例做分析,先給一個簡單的例子,完成與signal實例同樣的功能:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int signo)
{
switch(signo) {
case SIGINT:
printf("handle SIGINT by user\n");
break;
default:
break;
}
}
int main(int argc, char **argv)
{
struct sigaction act;
printf("ctr-c will be handled by user, enter to continue..\n");
act.sa_flags = 0;
act.sa_handler = signal_handler;
sigaction(SIGINT, &act, NULL);
getchar();
printf("ctr-c will be ignore, enter to continue..\n");
act.sa_flags = 0;
act.sa_handler = SIG_IGN;
sigaction(SIGINT, &act, NULL);
getchar();
printf("ctr-c will handle by default, enter to continue..\n");
act.sa_flags = 0;
act.sa_handler = SIG_DFL;
signal(SIGINT, SIG_DFL);
getchar();
printf("thanks, bye!\n");
return 0;
}
運行測試:
$ ./sigaction
ctr-c will be handled by user, enter to continue..
^Chandle SIGINT by user // 信號被自定義函數獲取,此處與signal行爲不同,getchar函數被中斷
ctr-c will be ignore, enter to continue..
^C^C^C // 信號被忽略
ctr-c will handle by default, enter to continue..
^C // 默認處理,終端程序
信號發送函數
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
pid
:可能選擇有以下四種
1. pid大於零時,將信號發送給進程ID爲pid的進程。
2. pid等於零時,信號將送往所有與調用kill()的那個進程屬同一個使用組的進程。
3. pid等於-1時,信號將送往所有調用進程有權給其發送信號的進程,除了進程1(init)。
4. pid小於-1時,信號將送往以-pid爲組標識的進程。
sig
:信號值,若爲0則不發送任何信號。
kill
函數的使用實例:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void signal_handler(int signo)
{
switch(signo) {
case SIGUSR1:
printf("get signal -- SIGUSR1\n");
break;
default:
break;
}
}
int main(int argc, char **argv)
{
pid_t pid;
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = signal_handler;
sigaction(SIGUSR1, &act, NULL);
pid = getpid();
printf("send SIGUSR1 to curent process\n");
kill(pid, SIGUSR1);
getchar();
printf("thanks, bye!\n");
return 0;
}
運行測試:
$ ./kill
send SIGUSR1 to curent process
get signal -- SIGUSR1
thanks, bye!
本文代碼實例github地址:
https://github.com/zsirkg/myWorks/tree/master/signal