一、信号概念
1.1、概念:
信号是linux系统为了响应某些状况而产生的事件,进程收到信号后采用相应的动作。
信号是异步事件,当信号到达,保存当前进程的执行环境,转去执行信号处理函数,当信号处理函数执行完毕,恢复现场,继续执行。
1.2、哪些情况会引发信号?
1.键盘事件 ctrl +c ,ctrl + \(以段错误方式退出)
2.非法内存
3.硬件故障
4.从用户态到内核态的切换
1.3、系统有哪些信号?
查看Linux系统中信号的命令:kill -l
1.4、进程收到信号的三种处理方式:
1、默认处理方式(大多数信号的系统默认动作是终止信号)
2、忽略:信号来了,装着没看到。(无法忽略的信号:SIGKILL,SIGSTOP)
因为这两种信号是向内核和超级用户提供了让进程终止或停止的可靠方法(是系统最终保护措施)
3、捕获并处理:当信号来了,执行我们自己写的代码(无法捕获的信号:SIGKILL,SIGSTOP)
二、操作信号
2.1、注册信号:signal
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
- 1、函数功能:获取信号值为signum的信号,如果获取成功,则开始执行handler指向的函数
- 2、返回值:
- 返回成功:返回对应这个信号的以前的旧处理函数
- 返回错误:返回SIG_ERR
SIG_ERR的宏为:#define SIG_IGN ((sighandlen_t)-1)
- 3、参数列表
- 3.1、signum:信号名,除过SIG_KILL和SIG_STOP
- 3.2、 handler 的选项
- 1:自己定义函数
- 2:SIG_IGN(执行忽略处理) 实质:#define SIG_IGN ((sighandlen_t)-1)
- 3:SIG_DEL (执行默认处理) 实质:#define SIG_IGN ((sighandlen_t)0)
eg1:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
void handler(int n)
{
printf("挂了吧\n");
exit(1);
}
int main(void)
{
signal(SIGINT,SIG_IGN);
//SIGINT 表示ctrl + c发出的终止信号,SIGINT表示采取忽略措施
signal(SIGQUIT,handler);
//SIGQUIT表示ctrl+\段错误终止发出的信号,收到信号后执行handler函数
int i= 0;
for(i = 0;i<20;i++)
{
printf("皮皮皮,就是死不了\n");
sleep(1);
}
}
eg2:
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
int main()
{
int i=0;
signal(SIGINT,SIG_DFL);//SIGINT是ctl c发起的信号
//SIG_DFL表示采用默认处理方式处理接收到的信号,既ctrl +c 可终止信号
for(i=0;i<10;++i)//执行10秒
{
printf("皮皮皮,死不了\n");
sleep(1);
}
return 0;
}
2.2、信号分类
1、不可靠信号
Linux的信号继承自早期的UNIX信号
当时UNIX信号的缺陷有:
1.信号处理函数执行完毕,信号恢复成默认处理方式(linux已经改进)
2.会出现信号丢失,信号不排队
而早期的信号只有1-31号信号,它们会出现信号丢失现象
2、可靠信号
后期为了更安全有了34-64信号,它们为可靠信号,不会出现信号丢失,支持排队,信号处理函数执行完毕,不会恢复成缺省处理方式
2.3、发送信号
1、指令方式发送
kill -信号值 pid
2、函数方式发送
1、kill( )函数
int kill(int pid,int signum); 1、函数功能:将信号值为signum的信号发送给pid的进程或进程组 2、函数参数: pid > 0 :将该信号发送给进程ID为pid的进程 pid = 0 ::将该信号发送给调用者所有进程组的任何一个进程 pid = -1:该信号有权限发送给任何一个进程,除了1 pid < -1:发送给|pid|进程组的所有进程 signum:信号量值
3、返回值:
成功返回:0,失败返回:-1。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
void handler (int n)
{
printf("收到,收到\n");
}
int main()
{
signal(SIGUSR1,handler);//父进程收到子进程信号,执行handler函数
pid_t pid = fork();
if(pid == 0)
{
printf("子进程给父进程准备发送信号啊\n");
kill(getppid(),SIGUSR1);
//子进程给父进程发送SIGUSR1信号
exit(0);
}
else
{
while(1)
{
printf("收到请回答!\n");
sleep(1);
}
}
}
- 2、给本进程发送信号:raise( )函数
int raise(int signo)
- 返回值:成功返回0,不成功返回非0
- 调用raise()函数等价于调用kill()函数
- 3、给进程组发送信号
killpg(int pgrp,int signum);
- 如果pgrp为0,则将信号发送给调用进程的进程组。
- 如果pgrp小于或等于1,行为是未定义的。
- 返回值:成功返回0,失败返回-1
- 4、pause( )函数
int pause(void)
-函数作用:使进程挂起直到捕捉到一个信号
-返回值:-1,error设置为EINTR
三、常用的SIGALRM信号
- 1、alarm( )函数
int alarm(int sec)
-函数功能:当sec规定的时间到后,触发SIGALRM信号给进程,
如果sec是0,表示清除信号
-返回值:
成功:如果以前设置过alarm(sec)函数
则返回以前设置闹钟时间的剩余时间,没有设置过则返回0;
失败:返回-1
eg:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
void handler(int n)
{
printf("超时!\n");
exit(1);
}
int main(void)
{
int num = 0;
signal(SIGALRM,handler);
//SIGALRM 当alarm设置的时间已经超时时,产生该信号,执行handler函数
printf("输入学号:");
alarm(5);
//5s后没有清除SIGALRM函数,则会触发SIGALRM信号
scanf("%d",&num);
alarm(0);
printf("学号是:%d\n",num);
for(;;)//验证alarm(0)是否清除了SIGALARM
{
fflush(stdout);
printf(".");
sleep(1);
}
}
- 2、简单数学考试计时程序
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
int Right = 0;//做对题数
int Wrong = 0;//做错题数
void handler(int n)
{
printf("时间到!\n");
printf("Right = %d,Wrong = %d\n",Right,Wrong);
exit(1);
}
int main(void)
{
int i = 0;
signal(SIGALRM,handler);
srand(getpid());//以当前进程的pid作为产生随机时间的种子
alarm(20);//设置倒计时20s
for(i = 0;i < 10;i++)
{
int left = rand()%10;
int right = rand()%10;
printf("%d + %d = ",left,right);
int sum = 0;
scanf("%d",&sum);
if(sum == left + right)
{
++Right;
}
else
{
++Wrong;
}
}
printf("时间到:Right = %d,Left = %d\n",Right,Wrong);
return 0;
}