一、信號
1.信號的定義
是linux系統
爲了響應某些狀況而產生的事件
。進程收到信號後應該採取相應的措施
2. 哪些情況會引發信號?
1.鍵盤事件,如ctrl+ c ,ctrl+/(結束程序)
2.非法內存
3.硬件故障
4.環境切換
3.如何查看信號
通過指令
kill -l
查看
4.信號的默認處理方式
man 7 signal
查看
5.常用信號解釋
SIGINT | ctrl+c發出的信號 |
---|---|
SIGOUIT | ctrl+\發出的信號 |
SIGUSER1 | 用戶自定義信號,常用於發送與獲取 |
SIGALRM | 鬧鐘信號,用於倒計時 |
SIGPWR | 關機,默認動作爲終止進程 |
SIGSTOP | 停止進程的執行,默認作爲暫停處理 |
SIGKILL | 無條件終止進程 |
SIGBUS | 非法訪問內存地址,默認爲終止進程併產生core文件 |
SIGHUP | 用戶退出shell,默認終止進程 |
SIGSEGV | 進程進行了無效的內存訪問 ,默認動作爲終止進程 |
SIGPIPE | 向一個沒有讀端的管道寫數據 |
SIGTERM | 請求終止進程,kill命令缺省發送 |
SIGIO | 此信號向進程指示發出一個異步IO事件 |
SIGWINCH | 窗口大小改變時發出 |
SIGXCPU | CPU時間限制超時 |
SIGXFSZ | 進程超過文件大小限制 |
SIGTRAP | 實現相關硬件異常 |
SIGILL | 非法指令異常 |
SIGFPE | 數學相關的異常,如被0除 |
SIGABRT | 由調用abrot函數產生,進程非正常退出 |
6.進程收到信號的3種處理
1.默認:執行該信號的默認處理動作
2.忽略:信號來了不處理,丟掉信號SIGKILL SIGSTOP不可忽視
3.捕獲並處理:信號來了。執行自己寫的代碼,SIGKILL SIGSTOP不能捕獲
7.信號的分類
a.
不可靠信號
( 1-31不可靠):
linux的信號繼承自早期的Unix信號,
Unix信號的缺陷:
1.信號處理函數執行完畢,信號恢復成默認處理方式(linux已改進)
2.會出現信號丟失,信號不排隊
b.可靠信號
( 34-64信號):
不會出現信號丟失,支持排隊,信號處理函數執行完畢,不會恢復成缺省處理方式。
c.實時信號
:就是可靠信號
d.非實時信號
:就是不可靠信號
二、操作信號
1.註冊信號
(1)signal函數
函數原型:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
函數參數解釋
:
signum:指明瞭所要處理的信號的類型,可取除SIGKILL與SIGSTOP外的任一種信號
sighandler_t handeler:信號執行函數,可取三種方式獲得:
(1)自己定義函數
(2)SIG_IGN #define SIG_IGN ((sighandler_t)1) //忽略該信號
(3)SIG_DFL #define SIG_IGN ((sighandler_t)0) //默認處理
返回值
:成功時返回信號處理函數指針
失敗時返回SIG_ERR
SIG_ERR #define SIG_IGN((sighandler_t)(-1))
(2)代碼實現:
1
2 #include <stdio.h>
3 #include <signal.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6
7 void handler(int s)
8 {
9 printf("game over!\n");
10 exit(0);
11 }
12
13 int main()
14 {
15 //ctrl+c
16 signal(SIGINT,SIG_IGN);
17 //crl+\信號
18 signal(SIGQUIT,handler);
19
20 for(;;)
21 {
22 printf("living!\n");
23 sleep(1);
24 }
25 }
26
2.如何給某一進程發送信號
方式1:
kill -信號值 pid
方式2:
函數:
int kill(int pid,int signum)
返回值:成功返回0,失敗返回-1
參數解釋:
signum:信號值,及信號編號
pid:進程id,如下值:pid>0 ,發送給pid進程 pid=0;調用者所在的進程組的任何一個進程 pid=-1;有權發送的任何一個進程,除了1;
進程組
:進程組有若干個進程
用管道連接的進程,fork創建出來的父子進程屬於同一個進程組
後臺進程
:jobs 查看
fg %id 將後臺進程調到前臺(id是作業號,非進程號)
ctrl+c 只發給前臺進程
前臺進程
:ctrl+z 前臺轉後臺
直接以後臺方式啓動進程,(./a.out &)>a
代碼實現:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <signal.h>
5
6 void handler(int n)
7 {
8 printf("收到!\n");
9 }
10
11 int main( void )
12 {
13 signal(SIGUSR1, handler);//用戶自定義信號
14 pid_t pid = fork();//創建子進程
15 if (pid == 0 ) {
16 sleep(1);
17 kill(getppid(), SIGUSR1);
18 exit(0);
19 } else {
20 while ( 1 ) {
21 printf("^_^\n");
22 sleep(3);
23 }
24 }
25 return 0;
26 }
結果:
3.三個發送信號函數
給自己發信號:
int rasie(int signum)
給進程組發送信號:int killpg(int pgrp,int sig)
暫停進程,直到信號被打斷int pause(void)
三、SIGALRM信號
1.alarm函數
int alarm(int sec)
當sec規定的時間到了,觸發SIGALRM信號,如果sec是0,表示清除信號
alarm(0)清除鬧鐘
返回值:若調用此函數前,進程已經設置了鬧鐘時間,則返回上一個鬧鐘時間的剩餘時間,否則返回0,失敗返回-1
2.代碼實現:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void handler(int s) {
printf("超時\n");
exit(1);
}
int main( void )
{
char buf[100] = {};
printf("name:");
signal(SIGALRM, handler);
alarm(3);
scanf("%s", buf);
alarm(0); // 清除鬧鐘
printf("name=%s\n", buf);
for ( ; ; ) {
printf("OK\n");
fflush(stdout);
sleep(1);
}
}
j結果:
四:簡單的考試計時
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <unistd.h>
5
6 int X = 0;//錯誤數
7 int V = 0;// 正確數
8
9 void handler(int s) {
10 printf("時間到\n");
11 printf("V=%d, X=%d\n", V, X);
12 exit(1);
13 }
14
15 int main( void )
16 {
17 signal(SIGALRM, handler);
18 int i=0;
19 alarm(20);
20 srand(getpid());
21 for ( i=0; i<10; i++) {
22 int left = rand()%10;
23 int right = rand()%10;
24 printf("%d+%d=", left, right);
25 int ret;
26 scanf("%d", &ret);
27 if ( left + right == ret ) {
28 V++;
29 } else {
30 X++;
31 }
32 }
33
34 printf("finish!\n");
35 printf("V=%d, X=%d\n", V, X);
36 }
37
38
結果:
good night!