信号的处理方式是远远比信号的产生
当闹钟响了就知道时间到了,说明对应信号处理方法比信号产生更早
操作系统的信号处理方法在编写操作系统的时候就已经编写好了
signal函数
1.1所有的信号
1.2 signal函数的概念和简单使用
捕捉信号就是自定义对应的信号的处理方法
9号信号杀死进程;不可以被捕捉,因为如果被捕捉,那么对应进程就是无敌的不能被杀死
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
void headler(int signo)
{
printf("signal NO.%d change\n",signo);
}
int main()
{
signal(2,headler);//函数名不加()就是一个函数指针
while(1)
{
printf("hello world pid: %d\n",getpid());
sleep(1);
}
return 0;
}
执行结果:ctrl+c发送二号信号,二号信号默认是终止进程
2.信号的产生方式
2.1.键盘产生
Ctrl+c 2号信号
Ctrl+\ 3号信号
Ctrl+z 20号信号
给对应进程发对应信号,命令格式:kill -信号编号 进程的pid
2.2程序奔溃,OS给进程发信号
代码中有一个除零错误
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
void headler(int signo)
{
printf("signal NO.%d change\n",signo);
}
int main()
{
int i=1;
while(i<=31){//捕获“”所有”信号
signal(i,headler);
i++;
}
int tem=10;//除零错误
tem/=0;
return 0;
}
执行错误:会发送一个8号信号
2.3.系统调用
kill:给任意一个进程发任意信号
raise:给当前进程发信号
2.4软件条件
概念:通过某种软件(OS),来触发信号的发送,定时器或者某种操作达到条件不就绪等这样等场景,来触发信号发送;
定时器或者某种操作达到条件不就绪:比如管道的读端不写且关闭读端,那么就会向写端发送SIGPIPE信号
2.alarm定时器
2.4.1.可以使用alarm证明CPU的计算速度远大于打印的速度
1s中count计算打印了多少次;
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
int main()
{
int count=0;
alarm(1);
while(1){
count++;
printf("count: %d\n",count);
}
return 0;
}
1s中count计算会有多少次
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
int count=0;
void handler(int signo)
{
printf("count: %d\n",count);
}
int main()
{
alarm(1);
signal(14,handler);
while(1)
{
count++;
}
return 0;
}
执行结果:可以证明CUP计算速度远大于打印速度
3.OS如何识别信号
实际执行信号的处理动作称为信号递达(Delivery) ;
信号从产生到递达之间的状态,称为信号未决(Pending);(接受到信号了,但是还没有处理)
进程可以选择阻塞 (Block )某个信号;(保持这个信号为未决)
识别信号
先看block位图(也叫信号屏蔽字)是否被阻塞;
如果没有阻塞再看pending位图是否接收到信号;
如果接收到信号再看handler函数指针数组按SIG_DFL(默认)、SIG_IGN(忽略)、具体的函数指针就是自定义执行
4.信号集操作函数
4.1.sigset_t的接口
sigset_t是一个位图结构
#include<signal.h>
i nt sigemptyset(sigset_t *set);//初始化对象,全为设为为0
int sigfillset(sigset_t *set);//把所有信号置为1
int sigaddset (sigset_t *set, int signo);//把几号信号为1
int sigdelset(sigset_t *set, int signo);//把几号信号为0
int sigismember(const sigset_t *set, int signo); //判断是否有几号信号,返回真1假0
4.2.sigprocmask
调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)。
#include<signall.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset); 返回值:若成功则为0,若出错则为-1
4.3.sigpending
#include<signal.h>
sigpending(sigset_t* set);
读取当前进程的未决信号集
4.4.简单使用
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
void show(sigset_t* set)
{
int i=1;
while(i<32)
{
if(sigismember(set,i))//信号为1则为真
printf("1");
else
printf("0");
i++;
}
printf("\n");
}
int main()
{
sigset_t iset,pending;
sigemptyset(&iset);//初始化
sigaddset(&iset,2);//添加2号信号
sigprocmask(SIG_SETMASK,&iset,NULL);//把信号屏蔽字改为iset
while(1){
sigemptyset(&pending);//初始化
sigpending(&pending);//获取pending位图
show(&pending);
sleep(1);
}
return 0;
}
执行结果:屏蔽了2号信号,2号信号是未决的
5.信号的捕捉的全过程和信号的处理时机
信号的处理时机:从内核态返回到用户态,做信号的检测并处理;
6.volatile
volatile:告诉编译器,不要优化被volatile修饰的变量
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<iostream>
using namespace std;
int main()
{
const int t=10;
int* p=const_cast<int*>(&t);
*p=20;
printf("t: %d\n",t);
printf("*p: %d\n",*p);
return 0;
}
执行结果:t被const修饰,编译器去t的值不会取内存中取
volatile const int t=10;//既可以解决
7.SIGCHLD信号
SIGCHLD:当子进程退出会给父进程发17号信号SIGCHLD
#include<unistd.h>
#include<signal.h>
#include<iostream>
using namespace std;
void handler(int signo)
{
cout<<signo<<endl;
cout<<getpid()<<endl;
}
int main()
{
signal(SIGCHLD,handler);
if(fork()==0)
{
int cnt=5;
while(cnt)
{
cout<<"I am child process, "<<getpid()<<endl;
cnt--;
sleep(1);
}
return 0;
}
while(1);
}
可以使用下面代码替换上面的signal(SIGCHLD,handler);就可以做到在不需要子进程的退出信息时自动释放
signal(SIGCHLD,SIG_IGN);