信号概念
每个信号用一个整形常量宏表示,以SIG开头
定义在头文件
中
man 7 signal
查看信号的默认行为
2号信号 Ctrl+c SIGINT
3号信号 Ctrl+\ SIGQUIT
信号都是异步的
忽略2号信号(命令行效果)
不是真正的忽略,而是把行为换成了\n
1 #include
✹ 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号信号
设置宏SIG_IGN
1 #include
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)//设置宏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
多个信号的执行行为
1 #include
2 //模拟sigfunc执行很长时间,所以使用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 }
不同信号到来时,可以打断其他信号的信号处理流程
当前信号到来时,如果正处于当前信号的信号处理流程,不会打断
最多再执行一次
任何信号都具有唤醒功能
//先按Ctrl+c 很快再按Ctrl+\
//效果: 会打断2号信号,进入3号信号的处理,相当于递归的进入另一个信号处理函数
✘ 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
原理 : 实际相当于一个数组,每个信号占据一个元素,当收到2号信号时,将对应元素位置置为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,转去执行处理
3号信号来,将1置为0,转去执行处理,打断2号处理函数
2号信号再来,置为1,此时,整体再2号信号处理中,不会将3号信号打断
前两个信号 2 3 处理完,再次检测数组,2号仍为1,则再置为0,转去执行2号信号处理函数
信号 2 3 3
^Cbefor sleep 2 is coming//2号
^\befor sleep 3 is coming
^\after sleep 3 is coming//同一信号再来,也在2号信号执行内部
befor sleep 3 is coming
after sleep 3 is coming
after sleep 2 is coming//2号
恢复信号默认处理
signal(SIGINT,SIG_DFL)
在read阻塞等待读取的状态下,也是可以接收信号的
sigaction信号处理机制
sigaction信号处理注册
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
//signum 是要捕捉的信号
//act是一个结构体
//oldact直接用NULL
//sigaction结构体
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实现signal的效果
1 #include
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结构体清空
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捕捉多个信号执行
1 #include
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结构体清空
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
设置为 SA_RESETHAND
处理完毕要捕捉的信号之后,自动撤销信号处理函数的注册
信号处理行为只有一次生效
12 struct sigaction act;
13 //将act结构体清空
14 bzero(&act,sizeof(act));
15 act.sa_flags=SA_SIGINFO|SA_RESETHAND;
16 act.sa_sigaction=sigfunc;
设置为 SA_NODEFER
信号不会再丢失,信号不断的打断,会打断自己
12 struct sigaction act;
13 //将act结构体清空
14 bzero(&act,sizeof(act));
15 act.sa_flags=SA_SIGINFO|SA_NODEFER;
16 act.sa_sigaction=sigfunc;
真 · 递归调用
信号不会丢失
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
设置为 SA_RESTART
当发生信号时,正阻塞在某系统调用,设置此参数,立即进入信号的处理,处理完毕之后,继续处理该阻塞
1 #include
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结构体清空
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结构体成员 sigset_t sa_mask 将要被阻塞的信号集合
//系统提供对sigset_t结构体的接口
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
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结构体清空
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
取出当前内核中挂起的信号
必须写在当前正在执行的信号处理函数内,否则,当前信号处理完毕,阻塞信号得到响应边查不到
✹ 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);//从内核中把挂起的信号取出
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号处理过程中发送3号信号 3号信号挂起
^\SIGQUIT is pending
after sleep 2 is coming
before sleep 3 is coming
after sleep 3 is coming
关键代码若不希望被打断,实现保护代码,借助 sigprocmask信号阻塞(全程阻塞)
sigprocmask信号阻塞
1 #include
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信号发送函数
5 //kill(pid,sig)
6 //pid 为要接收信号的进程pid 可以通过getpid获取
7 //sig 为要发送的信号
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");
//发送SIGQUIT信号把自己杀死
kill(getpid(),SIGQUIT);
计时器与信号
睡眠函数
sleep();//以秒为单位
usleep();//以微秒为单位
sleep函数内部是用信号机制处理的
alarm(seconds);
seconds秒后自动产生一个SIGALRM信号
计时统计
1 #include
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阻塞
虚拟计时器 :
实用计时器 : 实际运行在内核态和用户态,不包括read阻塞
使用真实计时器
1 #include
2
? 3 void sigfunc(int signum)
4 {
5 //获取当前时间并打印
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;//重复间隔
18 sigfunc(0);
19 int ret=setitimer(ITIMER_REAL,&t,NULL);
20 ERROR_CHECK(ret,-1,"setitimer");
21 while(1);
22 return 0;
23 }
真实计时器,程序执行是直观感受到的时间,阻塞也计算在内
使用实用计时器
1 #include
2
✹ 3 void sigfunc(int signum)
4 {
5 //获取当前时间并打印
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;//重复间隔
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
//实用计时器,不统计read阻塞
hello world
hello world
//直到不阻塞才继续统计
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;//重复间隔
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;