基本概念
信号是系统响应某个条件而产生的事件,进程接收到信号会执行相应的操作。与信号有关的系统调用在“signal.h”头文件中有声明。
系统调用kill()发送信号给进程,进程响应方式有三种:1.默认2.忽略3.自定义,这三种方式用signal()可以改变。
信号的产生
一个事件对应一个信号。
![](https://i-blog.csdnimg.cn/blog_migrate/1322e4e6a69cffe6b471da24947a9d8d.png)
大多数情况为出现了某些异常,产生中断。
![](https://i-blog.csdnimg.cn/blog_migrate/c536195128b993ef29e67bda0fd3bb77.png)
信号的值是一个数字,它在程序中定义为一个宏,用数字表示几号信号,常见的有:
#define SIGINT 2 //键盘按下 Ctrl+c 时,会产生该信号
#define SIGKILL 9 //该信号的响应方式不允许改变
#define SIGSEGV 11//访问空指针
#define SIGPIPE 13 //读端关闭的描述符,写端写入时产生,该信号会终止程序
#define SIGTERM 15 //系统 kill 命令默认发送的信号
#define SIGCHLD 17 //子进程结束后,会默认给父进程发送该信号
信号可以用来表示产生了什么错误,使用它便于我们查错
修改信号响应方式
使用signal()修改响应方式如下,其中传入handler的有三种:
1.SIG_IGN忽略
2.SIG_DFL默认
3.自己写的信号处理函数自定义
signal(信号类型,函数指针)
![](https://i-blog.csdnimg.cn/blog_migrate/18da90a037d94f743af0ceda8eed728e.png)
自定义
ctrl+c可以终止当前进程,它的信号是SIGINT,下面来改变一下它的响应使其输出2:
void sig_fun(int sig)
{
printf("sig=%d\n",sig);
}
int main()
{
signal(SIGINT,sig_fun);//也可以写signal(2,sig_fun)
while(1)
{
printf("hello\n");
sleep(1);
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/0cd209f02fbba9fbfbc6d14b8aeda84f.png)
signal是一个设置,执行过了就是设定好了,当遇到这个信号时才以这个设定方式响应。
忽略
默认:SIG_DFL :(void(*)(int)) 0
忽略:SIG_IGN: (void(*)(int)) 1
int main()
{
signal(SIGINT,SIG_IGN);//SIGINT变FIG_IGN忽略
while(1)
{
printf("hello\n");
sleep(1);
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/a3ee73adcf3f638ff90bd55cb75b969f.png)
多次修改
响应执行的是最后一次设定,如果设置两次,如下,则会忽略信号SIGINT
![](https://i-blog.csdnimg.cn/blog_migrate/f76e7f15c0241dc6b8e6aa2a7c49f1c1.png)
如果要第一次遇到2信号用自定义方式响应,第二次默认,可以在自定义响应中再重新定义一次
void sig_fun(int sig)
{
printf("sig=%d\n",sig);
signal(sig,SIG_DFL);//下一次调用sig_fun时用默认响应
}
int main()
{
signal(SIGINT,sig_fun);//也可以写signal(2,sig_fun)
while(1)
{
printf("hello\n");
sleep(1);
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/6b62ac7409497e69d9958c376fd9a9f3.png)
发送信号kill()
kill需要获取pid和信号代号两个参数,返回-1失败,0成功
![](https://i-blog.csdnimg.cn/blog_migrate/468c9543831240250aa9176195ad6a81.png)
自己写一个使用kill
int main(int argc ,char*argv[])
{
if(argc != 3)
{
printf("arg err");
exit(0);
}
int sig = 0;
int pid = 0;
sscanf(argv[1],"%d",&pid);//把输入的东西以整形的方式传给pid
sscanf(argv[2],"%d",&sig);
if(kill(pid,sig)==-1)//用kill
{
printf("kill err\n");
}
exit(0);
}
运行一下,给上面那个main程序发送信号2
![](https://i-blog.csdnimg.cn/blog_migrate/a9ce244d8c3d99251234cc2981474bff.png)
kill -9为强制结束,不能更改
信号实现
信号在PCB中,一开始全是0,32位可以收32个信号,64位收64个。进程什么信号都没有接收,kill发送信号时先根据pid找到那个进程,然后根据信号的值偏移,如果是信号2,则第三位上0变成1如下图。处理完信号后该位置再变回0.
![](https://i-blog.csdnimg.cn/blog_migrate/a0728bb95048705a47e373b0ce72cc50.png)
如果短时间内多次发送同一个信号,则可能只响应一次,该位置上1还没变回来。
发送信号处理僵尸进程
子进程结束 内核发送信号SIGCHLD给父进程
默认情况下是忽略了这个信号的,不做任何处理,所以会产生僵尸进程;
可以自定义响应:如果将接收到这个信号后使用wait(),解决僵尸进程。
也可以用忽略,父进程不需要知道返回值;(unix不可行,只有linux系统可以)
void fun(int sig)
{
wait(NULL);
}
int main()
{
//signal(SIGCHLD,fun);//自定义处理僵尸进程
signal(SIGCHLD,SIG_IGN);//忽略子进程返回
int n=0;
pid_t pid=fork();
if(pid==-1)exit(0);
if(pid==0)
{
s="child";
n=3;
}
else
{
s = "parent";
n=7;
}
for(int i=0;i<n;i++)
{
printf("s=%s\n",s);
sleep(1);
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/7d27c13b1f3b9d5a27b90b04d8eb063d.png)