信號概念
每個信號用一個整形常量宏表示,以SIG開頭
定義在頭文件<signal.h>中
man 7 signal查看信號的默認行為
2號信號 Ctrl+c SIGINT
3號信號 Ctrl+\ SIGQUIT
信號都是異步的
忽略2號信號(命令行效果)
不是真正的忽略,而是把行為換成了\n
1 #include <func.h>
? 2 void sigfunc(int signum)
3 {
4 //printf("%d is coming\n",signum);
5 printf("\n");
6 }
7
? 8 int main(int argc,char*argv[])
9 {
10 if(signal(SIGINT,sigfunc)==SIG_ERR)
11 {
12 perror("signal\n");
13 }
14 while(1);
15
16 return 0;
17 }
wenrou@uDesktop ~/workplace/LinuxDay10 ./signal
^C
^C
^C
真正的忽略2號信號
設(shè)置宏SIG_IGN
1 #include <func.h>
2 void sigfunc(int signum)
3 {
4 printf("%d is coming\n",signum);
5 }
6
? 7 int main(int argc,char*argv[])
8 {
9 if(signal(SIGINT,SIG_IGN)==SIG_ERR)//設(shè)置宏SIG_IGN
10 {
11 perror("signal\n");
12 }
13 while(1);
14
15 return 0;
16 }
wenrou@uDesktop ~/workplace/LinuxDay10 ./signal
^C^C^C^C^C^C
多個信號的執(zhí)行行為
1 #include <func.h>
2 //模擬sigfunc執(zhí)行很長時間,所以使用sleep
3 void sigfunc(int signum)
4 {
5 printf("befor sleep %d is coming\n",signum);
6 sleep(3);
7 printf("after sleep %d is coming\n",signum);
8 }
9
? 10 int main(int argc,char*argv[])
11 {
12 //注冊2號信號
13 if(signal(SIGINT,sigfunc)==SIG_ERR)
14 {
15 perror("signal\n");
16 }
17 //注冊3號信號
18 if(signal(SIGQUIT,sigfunc)==SIG_ERR)
19 {
20 perror("signal\n");
21 }
22 while(1);
23
24 return 0;
25 }
不同信號到來時,可以打斷其他信號的信號處理流程
當(dāng)前信號到來時,如果正處于當(dāng)前信號的信號處理流程,不會打斷
最多再執(zhí)行一次
任何信號都具有喚醒功能
//先按Ctrl+c 很快再按Ctrl+\
//效果: 會打斷2號信號,進入3號信號的處理,相當(dāng)于遞歸的進入另一個信號處理函數(shù)
? wenrou@uDesktop ~/workplace/LinuxDay10 ./signal
^Cbefor sleep 2 is coming//不同信號到來
^\befor sleep 3 is coming
after sleep 3 is coming
after sleep 2 is coming
^Cbefor sleep 2 is coming//同一信號到來
^C^C^C^C^C^C^C^Cafter sleep 2 is coming
befor sleep 2 is coming
after sleep 2 is coming
原理 : 實際相當(dāng)于一個數(shù)組,每個信號占據(jù)一個元素,當(dāng)收到2號信號時,將對應(yīng)元素位置置為1,處理該信號時,將該位置為0,
信號 2 3 2
^Cbefor sleep 2 is coming
^\befor sleep 3 is coming
^Cafter sleep 3 is coming
after sleep 2 is coming
befor sleep 2 is coming
after sleep 2 is coming
3號信號會將2號信號打斷,再按2號信號,此時整體是在2號信號里面,所以后面的2號信號不會將3號信號打斷
原理
2號信號到來,將1置為0,轉(zhuǎn)去執(zhí)行處理
3號信號來,將1置為0,轉(zhuǎn)去執(zhí)行處理,打斷2號處理函數(shù)
2號信號再來,置為1,此時,整體再2號信號處理中,不會將3號信號打斷
前兩個信號 2 3 處理完,再次檢測數(shù)組,2號仍為1,則再置為0,轉(zhuǎn)去執(zhí)行2號信號處理函數(shù)
信號 2 3 3
^Cbefor sleep 2 is coming//2號
^\befor sleep 3 is coming
^\after sleep 3 is coming//同一信號再來,也在2號信號執(zhí)行內(nèi)部
befor sleep 3 is coming
after sleep 3 is coming
after sleep 2 is coming//2號
恢復(fù)信號默認處理
signal(SIGINT,SIG_DFL)
在read阻塞等待讀取的狀態(tài)下,也是可以接收信號的
sigaction信號處理機制
sigaction信號處理注冊
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
//signum 是要捕捉的信號
//act是一個結(jié)構(gòu)體
//oldact直接用NULL
//sigaction結(jié)構(gòu)體
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
使用sigaction實現(xiàn)signal的效果
1 #include <func.h>
2
? 3 void sigfunc(int signum,siginfo_t *p,void *p1)
4 {
5 printf("%d is coming\n",signum);
6 }
7
? 8 int main(int argc,char*argv[])
9 {
10 struct sigaction act;
11 //將act結(jié)構(gòu)體清空
12 bzero(&act,sizeof(act));
13 act.sa_flags=SA_SIGINFO;
14 act.sa_sigaction=sigfunc;
15 int ret;
16 ret=sigaction(SIGINT,&act,NULL);
17 ERROR_CHECK(ret,-1,"sigaction");
18 while(1);
19
20 return 0;
21 }
? wenrou@uDesktop ~/workplace/LinuxDay10 ./sigaction
^C2 is coming
^C2 is coming
^C2 is coming
^C2 is coming
使用sigaction捕捉多個信號執(zhí)行
1 #include <func.h>
2
? 3 void sigfunc(int signum,siginfo_t *p,void *p1)
4 {
5 printf("before sleep %d is coming\n",signum);
6 sleep(3);
7 printf("after sleep %d is coming\n",signum);
8 }
9
? 10 int main(int argc,char*argv[])
11 {
12 struct sigaction act;
13 //將act結(jié)構(gòu)體清空
14 bzero(&act,sizeof(act));
15 act.sa_flags=SA_SIGINFO;
16 act.sa_sigaction=sigfunc;
17 int ret;
18 //注冊2號信號
19 ret=sigaction(SIGINT,&act,NULL);
20 ERROR_CHECK(ret,-1,"sigaction");
21 //注冊3號辛哈
22 ret=sigaction(SIGQUIT,&act,NULL);
23 ERROR_CHECK(ret,-1,"sigaction");
24 while(1);
25
26 return 0;
27 }
信號 2 3 2
//與signal效果一樣
wenrou@uDesktop ~/workplace/LinuxDay10 ./sigaction
^Cbefore sleep 2 is coming
^\before sleep 3 is coming
^Cafter sleep 3 is coming
after sleep 2 is coming
before sleep 2 is coming
after sleep 2 is coming
sa_flags
設(shè)置為 SA_RESETHAND
處理完畢要捕捉的信號之后,自動撤銷信號處理函數(shù)的注冊
信號處理行為只有一次生效
12 struct sigaction act;
13 //將act結(jié)構(gòu)體清空
14 bzero(&act,sizeof(act));
15 act.sa_flags=SA_SIGINFO|SA_RESETHAND;
16 act.sa_sigaction=sigfunc;
設(shè)置為 SA_NODEFER
信號不會再丟失,信號不斷的打斷,會打斷自己
12 struct sigaction act;
13 //將act結(jié)構(gòu)體清空
14 bzero(&act,sizeof(act));
15 act.sa_flags=SA_SIGINFO|SA_NODEFER;
16 act.sa_sigaction=sigfunc;
真 · 遞歸調(diào)用
信號不會丟失
wenrou@uDesktop ~/workplace/LinuxDay10 ./sigaction
^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
after sleep 2 is coming
after sleep 2 is coming
after sleep 2 is coming
after sleep 2 is coming
after sleep 2 is coming
after sleep 2 is coming
設(shè)置為 SA_RESTART
當(dāng)發(fā)生信號時,正阻塞在某系統(tǒng)調(diào)用,設(shè)置此參數(shù),立即進入信號的處理,處理完畢之后,繼續(xù)處理該阻塞
1 #include <func.h>
2
? 3 void sigfunc(int signum,siginfo_t *p,void *p1)
4 {
5 printf("%d is coming\n",signum);
6 }
7
? 8 int main(int argc,char*argv[])
9 {
10 struct sigaction act;
11 //將act結(jié)構(gòu)體清空
12 bzero(&act,sizeof(act));
13 act.sa_flags=SA_SIGINFO|SA_RESTART;
14 act.sa_sigaction=sigfunc;
15 int ret;
16 //注冊2號信號
17 ret=sigaction(SIGINT,&act,NULL);
18 ERROR_CHECK(ret,-1,"sigaction");
19 //卡在讀取標準輸入
20 char buf[128];
21 read(STDIN_FILENO,buf,sizeof(buf));
22 printf("buf is %s\n",buf);
23 return 0;
24 }
? wenrou@uDesktop ~/workplace/LinuxDay10 ./sigaction
^C2 is coming
^C2 is coming
^C2 is coming
^C2 is coming
nihao
buf is nihao
sigaction結(jié)構(gòu)體成員 sigset_t sa_mask 將要被阻塞的信號集合
//系統(tǒng)提供對sigset_t結(jié)構(gòu)體的接口
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
1 #include <func.h>
2
? 3 void sigfunc(int signum,siginfo_t *p,void *p1)
4 {
5 printf("before sleep %d is coming\n",signum);
6 sleep(3);
7 printf("after sleep %d is coming\n",signum);
8 }
9
? 10 int main(int argc,char*argv[])
11 {
12 struct sigaction act;
13 //將act結(jié)構(gòu)體清空
14 bzero(&act,sizeof(act));
15 act.sa_flags=SA_SIGINFO;
16 act.sa_sigaction=sigfunc;
17 //我們將3號信號填入sa_mask
18 //1.清空sa_mask集合
19 sigemptyset(&act.sa_mask);
20 //2.把3號信號填入阻塞集合
21 sigaddset(&act.sa_mask,SIGQUIT);
22 int ret;
23 ret=sigaction(SIGINT,&act,NULL);
24 ERROR_CHECK(ret,-1,"sigaction");
25 ret=sigaction(SIGQUIT,&act,NULL);
26 ERROR_CHECK(ret,-1,"sigaction");
27 while(1);
28 return 0;
29 }
3號信號不會將2號信號打斷
wenrou@uDesktop ~/workplace/LinuxDay10 ./sigaction
^Cbefore sleep 2 is coming
^\^\^\after sleep 2 is coming
before sleep 3 is coming
after sleep 3 is coming
取出當(dāng)前內(nèi)核中掛起的信號
必須寫在當(dāng)前正在執(zhí)行的信號處理函數(shù)內(nèi),否則,當(dāng)前信號處理完畢,阻塞信號得到響應(yīng)邊查不到
? 3 void sigfunc(int signum,siginfo_t *p,void *p1)
4 {
5 printf("before sleep %d is coming\n",signum);
6 sleep(3);
7 sigset_t pend;//存儲掛起的信號集合
8 sigpending(&pend);//從內(nèi)核中把掛起的信號取出
9 if(sigismember(&pend,SIGQUIT))
10 {
11 printf("SIGQUIT is pending\n");
12
}
13 printf("after sleep %d is coming\n",signum);
14 }
wenrou@uDesktop ~/workplace/LinuxDay10 ./sigaction
^Cbefore sleep 2 is coming
after sleep 2 is coming
^Cbefore sleep 2 is coming //2號處理過程中發(fā)送3號信號 3號信號掛起
^\SIGQUIT is pending
after sleep 2 is coming
before sleep 3 is coming
after sleep 3 is coming
關(guān)鍵代碼若不希望被打斷,實現(xiàn)保護代碼,借助 sigprocmask信號阻塞(全程阻塞)
sigprocmask信號阻塞
1 #include <func.h>
2
? 3 int main(int argc,char*argv[])
4 {
5 sigset_t block;
6 sigemptyset(&block);
7 sigaddset(&block,SIGINT);
8 sigprocmask(SIG_BLOCK,&block,NULL);
9 sleep(10);//重要代碼
10 return 0;
11 }
wenrou@uDesktop ~/workplace/LinuxDay10 ./sigprocmask
^C^C^C^C^C%
接觸阻塞sigprocmask(SIG_UNBLOCK,&block,NULL)
kill信號發(fā)送函數(shù)
5 //kill(pid,sig)
6 //pid 為要接收信號的進程pid 可以通過getpid獲取
7 //sig 為要發(fā)送的信號
8 ARGS_CHECK(argc,2);
9 pid_t pid=atoi(argv[1]);
10 //SIGINT 即為Ctrl+c
11 int ret=kill(pid,SIGINT);
12 ERROR_CHECK(ret,-1,"kill");
//發(fā)送SIGQUIT信號把自己殺死
kill(getpid(),SIGQUIT);
計時器與信號
睡眠函數(shù)
sleep();//以秒為單位
usleep();//以微秒為單位
sleep函數(shù)內(nèi)部是用信號機制處理的
alarm(seconds);
seconds秒后自動產(chǎn)生一個SIGALRM信號
計時統(tǒng)計
1 #include <func.h>
2
3 int main(int argc,char*argv[])
4 {
5 struct timeval start,end;
6 gettimeofday(&start,NULL);
7 for(int i=0;i<100000000;i++);
8 gettimeofday(&end,NULL);
9 printf("use time=%ld\n",(end.tv_sec-start.tv_sec)*1000000+end.tv_usec-start.tv_usec);
10
11 return 0;
12 }
時鐘處理
真實計時器 : 直觀感受到的時間,包括read阻塞
虛擬計時器 :
實用計時器 : 實際運行在內(nèi)核態(tài)和用戶態(tài),不包括read阻塞
使用真實計時器
1 #include <func.h>
2
? 3 void sigfunc(int signum)
4 {
5 //獲取當(dāng)前時間并打印
6 time_t t;
7 time(&t);
8 printf("%s\n",ctime(&t));
9 }
10
? 11 int main(int argc,char*argv[])
12 {
13 signal(SIGALRM,sigfunc);
14 struct itimerval t;
15 bzero(&t,sizeof(t));
16 t.it_value.tv_sec=3;//初始間隔
17 t.it_interval.tv_sec=2;//重復(fù)間隔
18 sigfunc(0);
19 int ret=setitimer(ITIMER_REAL,&t,NULL);
20 ERROR_CHECK(ret,-1,"setitimer");
21 while(1);
22 return 0;
23 }
真實計時器,程序執(zhí)行是直觀感受到的時間,阻塞也計算在內(nèi)
使用實用計時器
1 #include <func.h>
2
? 3 void sigfunc(int signum)
4 {
5 //獲取當(dāng)前時間并打印
6 time_t t;
7 time(&t);
8 printf("%s\n",ctime(&t));
9 }
10
? 11 int main(int argc,char*argv[])
12 {
13 signal(SIGPROF,sigfunc);
14 struct itimerval t;
15 bzero(&t,sizeof(t));
16 t.it_value.tv_sec=3;//初始間隔
17 t.it_interval.tv_sec=2;//重復(fù)間隔
18 sigfunc(0);
19 int ret=setitimer(ITIMER_PROF,&t,NULL);
20 ERROR_CHECK(ret,-1,"setitimer");
21 char buf[128]={0};
22 read(STDIN_FILENO,buf,sizeof(buf));
23 printf("%s\n",buf);
24 while(1);
25 return 0;
26 }
? wenrou@uDesktop ~/workplace/LinuxDay10 ./setitimer_real
Wed Jan 27 21:33:30 2021
//實用計時器,不統(tǒng)計read阻塞
hello world
hello world
//直到不阻塞才繼續(xù)統(tǒng)計
Wed Jan 27 21:33:39 2021
Wed Jan 27 21:33:41 2021
Wed Jan 27 21:33:43 2021
^C
使用虛擬計時器
13 signal(SIGVTALRM,sigfunc);
14 struct itimerval t;
15 bzero(&t,sizeof(t));
16 t.it_value.tv_sec=3;//初始間隔
17 t.it_interval.tv_sec=2;//重復(fù)間隔
18 sigfunc(0);
19 int ret=setitimer(ITIMER_VIRTUAL,&t,NULL);
20 ERROR_CHECK(ret,-1,"setitimer");
21 sleep(5);
22 while(1);
23 return 0;