一,什麼是信號
1、信號是內容受限的一種異步通信機制
(1)信號的目的:用來通信
(2)信號是異步的(對比硬件中斷)
(3)信號本質上是int型數字編號(事先定義好的)
2、信號由誰發出
(1)用戶在終端按下按鍵
(2)硬件異常後由操作系統內核發出信號
(3)用戶使用kill命令向其他進程發出信號
(4)某種軟件條件滿足後也會發出信號,如alarm鬧鐘時間到會產生SIGALARM信號,向一個讀端已經關閉的管道write時會產生SIGPIPE信號
3、接收方如何處理
(1)忽略信號
(2)捕獲信號(信號綁定了一個函數)
(3)默認處理(當前進程沒有明顯的管這個信號,默認:忽略或終止進程)
二,Linux中常見信號介紹
在/usr/include/i386-linux-gnu/bits/signum.h
中定義
信號 | 編號 | 作用 |
---|---|---|
SIGINT | 2 | Ctrl+C時OS送給前臺進程組中每個進程 |
SIGABRT | 6 | 調用abort函數,進程異常終止 |
SIGPOLL SIGIO | 8 | 指示一個異步IO事件,在高級IO中提及 |
SIGKILL | 9 | 殺死進程的終極辦法 |
SIGUSR1 | 10 | 用戶自定義信號,作用和意義由應用自己定義 |
SIGUSR2 | 12 | 用戶自定義信號 |
SIGSEGV | 11 | 無效存儲訪問時OS發出該信號 |
SIGPIPE | 13 | 涉及管道和socket |
SIGALARM | 14 | 涉及alarm函數的實現 |
SIGTERM | 15 | kill命令發送的OS默認終止信號 |
SIGCHLD | 17 | 子進程終止或停止時OS向其父進程發此信號 |
三,進程對信號的處理
1、signal函數介紹
(1)signal函數綁定一個捕獲函數後信號發生後會自動執行綁定的捕獲函數,並且把信號編號作爲傳參傳給捕獲函數
(2)signal的返回值在出錯時爲SIG_ERR,綁定成功時返回舊的捕獲函數
2、用signal函數處理SIGINT信號
(1)默認處理
(2)忽略處理
(3)捕獲處理
3、代碼實踐:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
typedef void (*sighandler_t)(int);
void func(int sig)
{
if (SIGINT != sig)
return;
printf("func for signal: %d.\n", sig);
}
int main(void)
{
sighandler_t ret = (sighandler_t)-2;
//signal(SIGINT, func);
//signal(SIGINT, SIG_DFL); // 指定信號SIGINT爲默認處理
ret = signal(SIGINT, SIG_IGN); // 指定信號SIGINT爲忽略處理
if (SIG_ERR == ret)
{
perror("signal:");
exit(-1);
}
printf("before while(1)\n");
while(1);
printf("after while(1)\n");
return 0;
}
4、signal函數的優點和缺點
(1)優點:簡單好用,捕獲信號常用
(2)缺點:無法簡單直接得知之前設置的對信號的處理方法
5、sigaction函數介紹
(1)2個都是API,但是sigaction比signal更具有可移植性
(2)用法關鍵是2個sigaction指針
sigaction比signal好的一點:sigaction可以一次得到設置新捕獲函數和獲取舊的捕獲函數(其實還可以單獨設置新的捕獲或者單獨只獲取舊的捕獲函數),而signal函數不能單獨獲取舊的捕獲函數而必須在設置新的捕獲函數的同時才獲取舊的捕獲函數。
四,alarm和pause函數
1、alarm函數
(1)內核以API形式提供的鬧鐘
#include <stdio.h>
#include <unistd.h> // unix standand
#include <signal.h>
void func(int sig)
{
if (sig == SIGALRM)
{
printf("alarm happened.\n");
}
}
int main(void)
{
unsigned int ret = -1;
#if 0 //用signal函數來捕獲信號
signal(SIGALRM, func);
#endif
#if 1 //用sigaction函數來捕獲信號
struct sigaction act = {0};
act.sa_handler = func;
sigaction(SIGALRM, &act, NULL);
#endif
//測試alarm函數多次設置會抵消的效果
ret = alarm(5);
printf("1st, ret = %d.\n", ret);
sleep(3);
ret = alarm(5); // 返回值是2但是本次alarm會重新定5s
printf("2st, ret = %d.\n", ret);
sleep(1);
ret = alarm(5);
printf("3st, ret = %d.\n", ret);
while (1);
return 0;
}
實驗結果:
2、pause函數
(1)內核掛起,pause函數的作用就是讓當前進程暫停運行,交出CPU給其他進程去執行。噹噹前進程進入pause狀態後當前進程會表現爲“卡住、阻塞住”,要退出pause狀態當前進程需要被信號喚醒。
(2)代碼實踐使用alarm和pause來模擬sleep
#include <stdio.h>
#include <unistd.h> // unix standand
#include <signal.h>
void func(int sig)
{
}
void mysleep(unsigned int seconds)
{
struct sigaction act = {0};
act.sa_handler = func;
sigaction(SIGALRM, &act, NULL);
alarm(seconds);
pause();
}
int main(void)
{
printf("before mysleep.\n");
mysleep(3);
printf("after mysleep.\n");
pause();
return 0;
}