信號章節 -- 信號章節整體概要異步
信號基本概念函數
信號是異步事件,發送信號的線程能夠繼續向下執行而不阻塞。spa
信號無優先級。線程
1到31號信號是非實時信號,發送的信號可能會丟失,不支持信號排隊。3d
31號信號到64是實時信號, 發送的信號都會被接收, 支持信號排隊。blog
信號在Linux內核頭文件中的宏定義進程
信號的處理事件
因爲進程啓動時,SIGUSR1和SIGUSR2被忽略,通常咱們能夠在有須要時,去捕獲這兩個信號,進而調用本身的處理函數。相應的,咱們的程序其餘地方去發送相應的信號。資源
signal函數原型 以及使用時所要包含的頭文件get
和下面的是等價的:
實驗1 signal基本使用
實驗1.1
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
//定義信號處理函數
//signo: 進程捕獲到的信號
void sig_handler(int signo){
printf("%d, %d occured \n", getpid(), signo);
}
int main(){
#if 1 // 屏蔽這塊代碼,就是不捕獲這倆信號
//向內核登記信號處理函數以及信號值
if(signal(SIGTSTP, sig_handler) == SIG_ERR){
perror("signal error");
}
if(signal(SIGINT, sig_handler) == SIG_ERR){
perror("signal sigint error");
}
#endif
while(1){
sleep(1);
printf("- hello -\n");
};
return 0;
}
編譯運行
同時,根據這裏的打印也能夠看出,
SIGINT信號就是2號信號, 咱們在鍵盤上按下CTRL+C就能夠發送該信號了。
SIGTSTP信號就是20號信號,咱們在鍵盤上按下CTRL+Z就能夠發送該信號了。20號信號的備註就是 Keyboard stop, 即經過鍵盤發信號讓進程中止。
實驗1.2
若是屏蔽實驗1內捕獲這倆信號的代碼塊,運行效果以下
經常使用知識點補充:
19) SIGSTOP 20) SIGTSTP
19號信號和29號信號的相同點: 均可以使得進程暫停,而且收到SIGCONT信號後可讓進程從新運行。
19號信號和29號信號的不一樣點: SIGSTOP不能夠捕獲(即便用信號處理函數)。
那麼,咱們來讓剛纔中止的a.out繼續運行吧:
先查看a.out的pid
可見a.out的pid是8349
咱們經過kill來發SIGCONT信號(18號信號)讓a.out繼續運行
可見,a.out又繼續運行起來了,
然而,須要注意的是,經過18號信號被繼續執行的進程:當終端內按下CTRL+C,則不能使得該進程終止了;且按下CTRL+Z,終端內也毫無跡象;可是能夠經過kill -9被殺死。
根據實測,是這樣的,事實勝於雄辯。這個問題的緣由以及背後隱藏的暫時咱們還不知的相應知識點,能夠留待之後探索,咱們先暫且知道這麼一回事就好了。
實驗2 SIG_DFL 和 SIG_IGN 使用
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
//定義信號處理函數
//signo: 進程捕獲到的信號
void sig_handler(int signo){
printf("%d, %d occured \n", getpid(), signo);
}
int main(){
printf("pid: %d \n", getpid());
#if 1 // 屏蔽這塊代碼,就是不捕獲這倆信號
//向內核登記信號處理函數以及信號值
if(signal(SIGTSTP, SIG_IGN) == SIG_ERR){
perror("signal error");
}
if(signal(SIGINT, SIG_DFL) == SIG_ERR){
perror("signal sigint error");
}
#endif
while(1){
sleep(1);
printf("- hello -\n");
};
return 0;
}
此時,按下CTRL+Z對程序運行將毫無影響,而CTRL+C則採用默認方式,即結束進程。
實驗3 SIGUSR1 和 SIGUSR2 使用
注意,這兩個信號在進程啓動時默認是被忽略的。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
//定義信號處理函數
//signo: 進程捕獲到的信號
void sig_handler(int signo){
printf("%d, %d occured \n", getpid(), signo);
}
int main(){
printf("pid: %d \n", getpid());
#if 1 // 屏蔽這塊代碼,就是不捕獲這倆信號
//向內核登記信號處理函數以及信號值
if(signal(SIGUSR1, sig_handler) == SIG_ERR){
perror("signal error");
}
if(signal(SIGUSR2, sig_handler) == SIG_ERR){
perror("signal sigint error");
}
#endif
while(1){
sleep(1);
printf("- hello -\n");
};
return 0;
}
編譯運行,同時在另外一個終端內發送信號 kill -SIGUSR1 10452 、 kill -SIGUSR2 10452
實驗4
知識點:SIGKILL 和 SIGSTOP不能被忽略,也不能被捕獲。
本實驗將嘗試捕獲SIGKILL和SIGSTOP,並以SIG_IGN的方式進程處理。
核心代碼展現:
編譯運行將返回SIG_ERR,以下圖所示
實驗5 SIGCHLD
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
//定義信號處理函數
//signo: 進程捕獲到的信號
void sig_handler(int signo){
printf("%d, %d occured \n", getpid(), signo);
wait(NULL);
}
int main(void)
{
pid_t pid;
if(signal(SIGCHLD, sig_handler) == SIG_ERR){
perror("signal error");
}
pid = fork();
if (pid < 0)
{
printf("fork error");
}
else if (pid == 0) /* first child : 子進程 */
{
sleep(2);
printf("pid: child =%ld\n", (long)getpid());
exit(0);
}
// 使用信號的方式,父進程沒必要在此處阻塞調用wait,能夠繼續向下執行本身的任務。
while(1){
sleep(1);
printf("father can does his own things \n");
}
}
編譯運行:
實驗中可見,父進程收到了子進程的17號信號,17號信號就是SIGCHLD信號(或寫做SIGCLD)
使用信號來回收子進程後,父進程沒必要在阻塞調用wait,能夠繼續向下執行本身的任務。這個例子充分體現出了信號是一個異步事件。
在父進程還存活的期間,子進程退出將不會產生殭屍進程。
PS:父進程死後,確定不會有其子進程還仍然是殭屍進程,由於一個子進程們會在其父進程死後被1號進程領養,進而被1號進程回收掉所佔用的系統資源。
.