?專欄內(nèi)容:
postgresql內(nèi)核源碼分析
手寫數(shù)據(jù)庫toadb
并發(fā)編程
個(gè)人主頁:我的主頁
座右銘:天行健,君子以自強(qiáng)不息;地勢坤,君子以厚德載物.
================================
概述
信號(hào)是一種軟中斷的方式,讓進(jìn)程陷入中斷處理調(diào)用中;
linux 下信號(hào)也是一種進(jìn)程間通信的手段;進(jìn)程間也可以互相發(fā)送信號(hào),來傳遞狀態(tài),讓對(duì)方獲知,并處理一些事情。
信號(hào)種類
linux下信號(hào)種類很多 ,可以通過kill 命令來查詢
[senllang@localhost Dev]$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
這些信號(hào)分為兩部分,
1-31 是POSIX定義的可靠信號(hào),其中 SIGKILL,SIGSTOP 兩個(gè)信號(hào)不能被應(yīng)用程序捕獲處理,不能被阻塞,也不能忽略。
34-64 是POSIX定義的real-time使用的信號(hào),因?yàn)閷?shí)時(shí)性,可能會(huì)丟失,主要有RTMAX, RTMIN兩類;
信號(hào)處理函數(shù)
大多數(shù)信號(hào)可以被應(yīng)用程序捕獲,這樣就可以設(shè)置處理函數(shù)自定義處理行為。
信號(hào)的處理函數(shù),一般有三種:
- 自定義,通過設(shè)置函數(shù)進(jìn)行設(shè)置,當(dāng)信號(hào)產(chǎn)生時(shí)調(diào)用此回調(diào)函數(shù);
- SIG_DFL ,默認(rèn)值,執(zhí)行默認(rèn)行為;
- SIG_IGN ,忽略,不執(zhí)行任何動(dòng)作;
信號(hào)處理函數(shù)設(shè)置
自定義信號(hào)的處理函數(shù), 設(shè)置處理函數(shù)有兩個(gè)函數(shù)signal和sigaction ,它們定義如下:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
這個(gè)函數(shù)相對(duì)簡單,signum 就是要處理的信號(hào)值,handler就是自定義處理函數(shù),類型必須是 sighandler_t;
讓我們舉個(gè)例子看一下。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void MySigproc(int signumber)
{
printf("catch signal is %d, we process it now.\n",signumber);
}
int main()
{
signal(2,MySigproc);
signal(3,MySigproc);
signal(10,MySigproc);
signal(12,MySigproc);
do
{
/* code */
sleep(1000);
printf("signal break me dream.\n");
} while (1);
return 0;
}
獲后我們來運(yùn)行一下,看下效果
# 終端 1 運(yùn)行
[senllang@localhost signal]$ ./a.out
# 終端 2 發(fā)送信號(hào)
[senllang@localhost Dev]$ ps -ef|grep a.out
senllang 2700150 2035891 0 14:41 pts/1 00:00:00 ./a.out
senllang 2700164 2119038 0 14:41 pts/4 00:00:00 grep --color=auto a.out
[senllang@localhost Dev]$ kill -2 2700150
[senllang@localhost Dev]$ kill -3 2700150
[senllang@localhost Dev]$ kill -10 2700150
[senllang@localhost Dev]$ kill -12 2700150
# 終端 1 捕獲信號(hào)
catch signal is 2, we process it now.
signal break me dream.
catch signal is 3, we process it now.
signal break me dream.
catch signal is 10, we process it now.
signal break me dream.
catch signal is 12, we process it now.
signal break me dream.
最后按下Ctr+c已經(jīng)不起作用了,最后用kill -9 強(qiáng)制結(jié)束
另外設(shè)置函數(shù)提供的選項(xiàng)比較豐富,通過傳入sigaction結(jié)構(gòu)體參數(shù)來設(shè)置
#include <signal.h>
int sigaction(int signum,
const struct sigaction *_Nullable restrict act,
struct sigaction *_Nullable restrict oldact);
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 和 sa_sigaction 設(shè)置其一就可以;
- sa_handler 可以設(shè)置三種處理方式,默認(rèn),忽略,自定義
- sa_restorer 不是給應(yīng)用程序使用的,不用管;
- sa_mask 是設(shè)置那些信號(hào)位被阻塞,可以|多個(gè)信號(hào);
- sa_flags 是定義一些行為;
信號(hào)阻塞
信號(hào)阻塞,在設(shè)置信號(hào)阻塞掩碼sigmask, 置1的信號(hào)在產(chǎn)生后,處于pending狀態(tài),即信號(hào)未決狀態(tài),
直到信號(hào)投遞,也就是信號(hào)處理函數(shù)被調(diào)用。
接口說明
sigprocmask 接口可以獲取 或者 設(shè)置 信號(hào)mask
#include <signal.h>
int sigprocmask(int how, const sigset_t *_Nullable restrict set,
sigset_t *_Nullable restrict oldset);
參數(shù)說明
- how 可以為
- SIG_BLOCK, set 中的信號(hào)為要阻塞的信號(hào),加到已有的阻塞信號(hào)中
- SIG_UNBLOCK, set 中的信號(hào)為要解除阻塞的信號(hào),從已有的阻塞信號(hào)中去除
- SIG_SETMASK, 將現(xiàn)有的阻塞信號(hào)替換為 set 指定的信號(hào)
set , 要修改的信號(hào)集
oldset, 不為空,獲取修改前的信號(hào)集
多線程時(shí),要用 pthread_sigmask ,功能一樣
下面舉例,先將所有信號(hào)阻塞,然后再將SIGINT信號(hào)解除阻塞
void blocksig()
{
sigset_t set;
sigset_t oldset;
sigfillset(&set);//所有比特位全部置為1,則信號(hào)會(huì)全部被阻塞
sigprocmask(SIG_BLOCK,&set,&oldset);
sigemptyset(&set);//初始化信號(hào)量集
sigaddset(&set, SIGINT);//將SIGINT添加到信號(hào)量集中
sigprocmask(SIG_UNBLOCK, &set, &oldset);
}
信號(hào)產(chǎn)生
信號(hào)可以是命令產(chǎn)生,如鍵盤按鍵,或kill 等;
也可以由程序控制,來給某個(gè)進(jìn)程發(fā)送信號(hào)。
發(fā)送信號(hào)
可以通過這些函數(shù)進(jìn)行信號(hào)發(fā)送
- raise 發(fā)送一個(gè)信號(hào)給調(diào)用線程
#include <signal.h>
int raise(int sig);
- kill 發(fā)送信號(hào)給指定pid的進(jìn)程,或者進(jìn)程組,或者所有進(jìn)程
#include <signal.h>
int kill(pid_t pid, int sig);
- 還有其它的,如 pidfd_send_signal, killpg, pthread_kill,tgkill, sigqueue等,用到時(shí)可以再研究。
等待信號(hào)
就是當(dāng)前進(jìn)程被阻塞,直到某個(gè)信號(hào)發(fā)生;
當(dāng)信號(hào)處理函數(shù)返回時(shí),當(dāng)前阻塞的進(jìn)程才繼續(xù)運(yùn)行
#include <unistd.h>
int pause(void);
#include <signal.h>
int sigsuspend(const sigset_t *mask);
我們舉個(gè)例子來看看
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void MySigproc(int signumber)
{
printf("catch signal is %d, we process it now.\n",signumber);
}
int main()
{
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigaddset(&set, SIGQUIT);
sigaddset(&set, SIGUSR1);
signal(SIGINT,MySigproc);
signal(SIGQUIT,MySigproc);
signal(SIGUSR1,MySigproc);
signal(SIGUSR2,MySigproc);
do
{
/* code */
sigsuspend(&set);
printf("signal break me dream.\n");
} while (1);
return 0;
}
通過兩個(gè)終端進(jìn)行演示
# 終端1 運(yùn)行結(jié)果
[senllang@localhost signal]$ ./a.out
catch signal is 12, we process it now.
catch signal is 10, we process it now.
catch signal is 3, we process it now.
catch signal is 2, we process it now.
signal break me dream.
# 終端2 發(fā)送信號(hào)
[senllang@localhost Dev]$ ps -ef|grep a.out
senllang 2723802 2035891 0 19:28 pts/1 00:00:00 ./a.out
senllang 2723806 2119038 0 19:28 pts/4 00:00:00 grep --color=auto a.out
[senllang@localhost Dev]$ kill -2 2723802
[senllang@localhost Dev]$ kill -3 2723802
[senllang@localhost Dev]$ kill -3 2723802
[senllang@localhost Dev]$
[senllang@localhost Dev]$ kill -10 2723802
[senllang@localhost Dev]$ kill -12 2723802
我們看到,在終端2不斷發(fā)送信號(hào),直到發(fā)送了等待的非掛起信號(hào)12后,才開始解除信號(hào)阻塞,并處理消息,并解除進(jìn)程阻塞
對(duì)于2,3,10是阻塞信號(hào),并沒有立即處理,而且阻塞信號(hào)只處理一次
進(jìn)階內(nèi)容
信號(hào)處理函數(shù)的注意事項(xiàng)
- 處理函數(shù)是可重入的
- 處理函數(shù)不是被應(yīng)用程序調(diào)用,而是有自己獨(dú)立的棧;
保護(hù)臨界區(qū)
當(dāng)在關(guān)鍵代碼段時(shí),我們不希望被中斷,不被干擾,在之前或之后再處理中斷。
一般會(huì)在關(guān)鍵代碼塊前阻塞信號(hào),然后執(zhí)行完關(guān)鍵代碼塊后獲取未決的信號(hào)進(jìn)行處理;
常見軟件信號(hào)
- 程序內(nèi)部錯(cuò)誤, 如除0,訪問非法內(nèi)存等;一般是abort , signal 6 SIGABRT信號(hào);
- 在終端運(yùn)行程序時(shí),按下Ctrl+c時(shí),程序強(qiáng)制退出,產(chǎn)生的是SIGINT信號(hào),如果沒有指定處理函數(shù),默認(rèn)就是退出程序;
子進(jìn)程繼承
- 當(dāng)通過 fork 生成的子進(jìn)程,會(huì)繼承父進(jìn)程的信號(hào)設(shè)置和處理函數(shù);
- 當(dāng)通過 execv 產(chǎn)生的子進(jìn)程,會(huì)重置信號(hào)設(shè)置;
進(jìn)程與線程的信號(hào)
- 進(jìn)程可以設(shè)置信號(hào)阻塞,線程同樣也可以,每個(gè)線程是獨(dú)立設(shè)置
- 進(jìn)程可以直接處理信號(hào);由kill 或 sigqueue 產(chǎn)生的信號(hào),指定了對(duì)應(yīng)的pid ;
- 線程也可以直接處理信號(hào); tgkill 或 pthread_kill 產(chǎn)生的信號(hào),指定了tid ;
結(jié)尾
非常感謝大家的支持,在瀏覽的同時(shí)別忘了留下您寶貴的評(píng)論,如果覺得值得鼓勵(lì),請(qǐng)點(diǎn)贊,收藏,我會(huì)更加努力!
作者郵箱:study@senllang.onaliyun.com
如有錯(cuò)誤或者疏漏歡迎指出,互相學(xué)習(xí)。文章來源:http://www.zghlxwxcb.cn/news/detail-540475.html
注:未經(jīng)同意,不得轉(zhuǎn)載!文章來源地址http://www.zghlxwxcb.cn/news/detail-540475.html
到了這里,關(guān)于linux 信號(hào)原理 信號(hào)處理設(shè)置signal, 信號(hào)發(fā)送kill,信號(hào)等待sigsuspend,信號(hào)阻塞sigprocmask,一網(wǎng)打盡信號(hào)使用的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!