并发
同步:事件是可以预知的
异步:(并发)结果不可预知,到来的动作不知道,事件的到来不知道,事件到来的结果不知道
事件什么时候到来不知道,到来的结果不知道
异步事件的处理:查询法,通知法
事件发生的频率比较低用通知法,反之用查询法
一、信号
信号概念
信号是软件层面的中断
信号的响应依赖于中断
signal()
core文件是一个程序的某一个现场,一般保存的是一个错误的现场
运行一些成些许的时候,有时候会遇到是否要发送错误报告这个提示,这个发送的就是一个程序的现场
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
void (*signal(int signum,void(*func)(int )))(int)//上面的函数抽象出来等价于这个函数
第二个参数是新的行为
返回值是旧的行为
signal() sets the disposition of the signal signum to handler, which is
either SIG_IGN, SIG_DFL, or the address of a programmer-defined
function (a “signal handler”).
第二个参数不是SIG_IGN(忽略),SIG_DFL(默认)就是一个函数的入口地址
例子:
#include <stdio.h>
#include <stdlib.h>
#include<signal.h>
#include <unistd.h>
int main()
{
int i;
for(i=0;i<10;i++)
{
write(1,"*",1);
sleep(1);
}
exit(0);
}
这个例子,我们在运行的时候,就是一秒打印一个*号
当我们在键盘上按下CTRL+c的时候,打印就会结束,这里的CTRL+C在这里就相当于SIGINT
当我们加上信号的使用,让程序忽略掉这个CTRL+C
#include <stdio.h>
#include <stdlib.h>
#include<signal.h>
#include <unistd.h>
int main()
{
int i;
signal(SIGINT,SIG_IGN);
for(i=0;i<10;i++)
{
write(1,"*",1);
sleep(1);
}
exit(0);
}
运行结果:可以看出当我们CTRL+C的时候,打印并没有结束掉
换一个行为:
#include <stdio.h>
#include <stdlib.h>
#include<signal.h>
#include <unistd.h>
static void int_handler(int s)
{
write(1,"!",1);
}
int main()
{
int i;
//signal(SIGINT,SIG_IGN);
signal(SIGINT,int_handler);
for(i=0;i<10;i++)
{
write(1,"*",1);
sleep(1);
}
exit(0);
}
运行结果:当按下ctrl+C的时候就会打印一个!号
当程序运行的时候,我们一直按着ctrl+c按键,那么程序很快就会运行完,不会花费10秒,所以可以知道,信号会打断阻塞的系统调用
(根据这一点,我们知道之前的程序有很多,有这方面的错误)
信号的不可靠
信号的行为不可靠,信号到来的时候,函数的执行,是内核来完成的
不可靠指的是,在处理这个行为的同时,又来了另外一个相同的信号,那么这个执行现场由于是内核来布置的,就很有可能不是一个现场
那第二次的执行现场就会把第一个冲掉了
第一个调用没结束,第二个调用就开始了
可重入函数
为了解决信号的不可靠
所有的系统调用,都是可重入的,反过来不对,一部分库函数也是可重入的,如memcpy
第一个调用没结束第二个嗲用就开始,会让第一个产生不可预料的结果
一个函数有可重入版本的函数,那么这个函数的指针一定是在静态区的
重入的概念:一个调用还没结束,第二个调用就开始了
信号的响应过程
内核位每一个进程维护了一个位图
以打印那个代码为例子来讲
mask屏蔽字的值一般情况下都是1
pending位图初始值都是0
如果时间片耗尽了,要抱着当前的执行现场扎进内核,比如说如果从来没有发出信号,就是不打印!那么就会一秒打印一个,其实在这个过程中,我们的程序也在不停的被打断,每次打断之后,都会将当前的执行现场进行压栈存放,然后到内核中的等待调度的就绪队列,进行排队,然后重新被调度,再执行过程又被打断,重复上述过程。
当我们从kernel态回到user态时,我们要做的工作是:
mask&pending
如果是初始状态的话,那么整个按位与的值都是0,就说明没有收到任何信号,机会回到刚刚被打断的位置继续执行。如果这个时候有一个信号到来,那么pending的某一个相对位置就会变成1,按位与就可以得到某一位是1,就说明收到一个信号,接下来就会将mask和pending相应位置都置为0,地址的指向机会发生改变,地址换成了你注册的那个信号的处理函数的入口地址,去执行这个函数,信号处理函数响应完以后就会换回原来的地址。然后把mask位置为1,pending位0,然后再从kernel态回到user态,还是会做按位与,说明刚刚的信号被响应掉了。
!!!标准信号:当多个信号到来的时候,先响应哪个信号是没有顺序的,就是说标准信号的响应没有严格的顺序
只有从kernel态回到user态,做了mask&pending后我们才可以看到信号,所以信号是依赖中断这个机制来进行的,信号是kernel回到user路上响应的。
信号从收到到响应有不可避免的延迟
思考:如何忽略掉一个信号的?标准信号为什么要丢失?
SIG_IGN的机制就是将相应信号的mask位一直置为0,即使这个信号来了,按位与还是0,是不会响应这个信号的,看不到这个信号
标准信号丢失位图
下图可知:
来多个信号位图多次置1,还是1,不会改变
但是实时信号不会丢失,来多个信号,可以知道来了多个信号,依次响应。