一.关于信号概念
1.信号是Linux所使用的进程间通信的最古老的方式。它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式 。一个完整的信号周期包括三个部分,信号的产生,信号在进程中的注册,信号在进程中的注销,执行信号处理函数。如下图所示:
这里的对信号产生注册和注销都是信号的内部机制,而不是信号函数完成的
2.对信号的处理动作有三种:
a. 忽略此信号
b.执行该信号的默认处理动作
c.捕捉信号(自由态)
3.查看系统定义中的信号列表: kill -l 命令。
每个信号都有一个编号和一个宏定义名称,这些宏定义名称可以在signal.h中找到,例如:其中有定义#define SIGINT 2
二.产生信号
1. 通过终端按键产生信号
如下代码:
#include<stdio.h>
#include<signal.h>
void hander(int sig)
{
printf("%d\n",sig);
}
int main()
{
signal(2,hander);
while(1)
{
printf("i am a proc\n");
sleep(1);
}
}
以上代码使用了signal函数用来捕捉信号,则可以说明的是:ctrl+c实现的是信号的2号信号SIGINT。
2.调用系统函数向进程发信号
介绍几个函数
- kill命令是调用kill函数实现的。kill函数可以给一个指定的进程发送指定的信号。
#include<signal.h>
int kill(pid_t pid,int signo);
- raise函数可以给当前进程发送指定的信号(自己给自己发信号)
#include<signal.h>
int raise(int signo);
如下两个代码实现raise函数的功能:
#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
#include<stdlib.h>
void hander(int sig)
{
printf("%d\n",sig);
}
int main()
{
signal(2,hander);
sleep(3);
while(1)
{
raise(2);
printf("i am a proc\n");
sleep(1);
}
}
结果如下图:
#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
#include<stdlib.h>
void hander(int sig)
{
printf("%d\n",sig);
}
int main()
{
signal(2,hander);
sleep(3);
raise(2);
while(1)
{
printf("i am a proc\n");
sleep(1);
}
}
结果如下图:
- abort函数可以使当前进程接收到信号而异常终止,像exit函数一样,abort函数总会成功,故无返回值
#include<stdlib.h>
void abort(void);
3.由软件条件产生信号
例如:模拟闹钟
调用alarm函数可以设定一个闹钟,告诉内核在seconds秒之后给当前进程发SIGALRM信号,该信号的默认动作是终止当前程序
该函数的返回值是0或者是以前设定的闹钟时间还余下的秒数
用以下代码实现alarm的功能:
代码1:
#include<stdio.h>
#include<unistd.h>
int main()
{
alarm(1);
int count=0;
for(;1;count++)
{
printf("count= %d\n",count);
}
return 0;
}
结果示意图:
###代码2:
#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
#include<stdlib.h>
#include<unistd.h>
int count=0;
void hander(int sig)
{
printf("count = %d\n",count);
}
int main()
{
signal(14 ,hander);
alarm(1);
while(1)
{
count++;
}
return 0;
结果示意图:
以上两个代码均是表示count 在1 秒累加的次数,从结果看出第二个数比第一个数大很多,所以说明输入输出流对计算的速度影响非常大
三.阻塞信号
1. 信号在内核中的表示
a.实际执行信号的处理动作叫信号递达(Delivery),信号从产生到递达的状态,称为信号未决(Pending).进程可以选择阻塞某个信号。
b.信号阻塞就不会被递达,除非信号先被未决,然后解除阻塞。
c.信号在那内核中的表示示意图:
2.信号集操作函数
#include<signal.h>
int sigemptyset(sigset_t *set);//初始化set所指向的信号集,使其所有的bit清零,表示该信号集不包含任何有效信号。
int sigfillset(sigset_t *set);//初始化set所指向的信号集
int sigaddset(sigset_t *set,int signo);//在信号集中添加某种信号
int sigdelset(sigset_t *set,int signo);//在信号集中删除某种信号
int sigismemset(const sigset_t *set,int signo);//是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,包含返回1,反之返回0;
3.sigprocmask
#include<signal.h>
int sigprocmask(int how,const sigset *set,sigset_t *oset);//可以读取和改进进程的信号屏蔽字(阻塞信号集)
4.sigpending
#include<signal.h>
int sigpending(sigset_t *set);//读取当前进程的未决信号集,通过set参数传出
使用以上函数模拟一个信号在内核中的表示
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void show(sigset_t* pending)
{
int i=1;
for(; i<32; i++)
{
if(sigismember(pending,i))
{
printf("1 ");
}
else
{
printf("0 ");
}
}
printf("\n");
}
int main()
{
sigset_t block;
sigset_t oblock;
sigemptyset(&block);
sigemptyset(&oblock);
sigfillset(&block);
sigfillset(&oblock);
sigaddset(&block,2);
sigprocmask(SIG_BLOCK,&block,&oblock);
sigprocmask(SIG_BLOCK,&oblock,NULL);
while(1)
{
sigpending(&block);
show(&block);
sleep(1);
}
return 0;
}
结果示意图:
程序运行时候,每秒钟都把各个信号的未决状态打印一遍,由于我们阻塞了SIGINT信号,故按CTRL+C将会使SIGINT信号处于未决状态,按CTRL+\则可以终止程序,因为SIGQUIT信号没有被阻塞。