int a=0;
7 int b=0;
8 int add(){
9 a++;
10 sleep(10);
11 b++;
12 return a+b;
13 }
int main(){
18 printf(“main—%d\n”,add()) ;//2
19 }
打印结果为2
睡眠10s钟在这10s期间来了一个信号,会触发回调函数它也会进行一个add进行相加然后也打印出来结果,按理论一个为2一个为4
$ ./compete
^Csignal----3
main—4
按cotrl +c会调用函数结果为3和4
int a=0;
7 int b=0;
8 int add(){
9 a++;
10 sleep(10);
11 b++;
12 return a+b;
13 }
W> 14 void sigcb(int signo){
15 printf(“signal----%d\n”,add());
16 }
17 int main(){
18 signal(SIGINT,sigcb);
19 printf(“main—%d\n”,add()) ;}
刚开始a=0,b=0;然后有个add(a++,sleep,b++,return a+b),
main()调用了add,首先a++ 变成1陷入睡眠,来了个信号signal(ctrl+c)也就是SIGINT也会掉用add进来后第一步也是a++然后此时a被加成2信号里面等待了10s钟之后b++,然后为1,然后打印出来就是3信号处理完毕回到正常流程然后继续进行b++操作这个时候此时的b被加成2了然后打印出来就是4.,在这中间对a++进行了一次争抢操作这种情况下就是因为 时序情况下而造成的数据竞争
竞态条件:因为运行时序而造成的数据竞争–导致数据二义性,
本质是函数中所完成的操作并非是原子性操作;并且操作的数据是全局数据,如果是局部变量则不会发生这种情况,全局性的操作不是原子性受保护的操作
则该函数称为不可重入函数:这个函数不能再多个时序下重复调用
如果一个函数中操作了全局性数据,并且这个操作不是原子性操作,并且这个操作不受保护则这个函数是一个不可重入函数
不可重入函数:不能再多个时序的运行中重复调用(重复调用有可能会造成数据的二义性)
可重入函数:在多个时序的运行中重复调用,不会造成异常影响(数据二义性问题)
不可重入函数:malloc/free
自主实现sleep函数体现竞态条件
可中断睡眠会被信号打断
void sigcb(int signo){
6 //什么都不做,进程不会退出
7 }
8 int mysleep(int nsec){
9 signal(SIGALRM,sigcb);//重新定义改变SIGALRM的处理方式不让它
10 //退出进程让它变成sigcb
11 alarm(nsec);//ns之后的信号
12 pause();//暂停
W> 13 }
14 int main(){
15 mysleep(3);
16 printf("--------\n");
17 return 0;
18 }
[chenyongjie@localhost signal]$ ./mysleep
结果为运行3秒后打印------
程序2
void sigcb2(int signo){
6 sleep(10);
7 }
8
W> 9 void sigcb(int signo){
10 //什么都不做,进程不会退出
11 }
12 int mysleep(int nsec){
13 signal(SIGALRM,sigcb);//重新定义改变SIGALRM的处理方式不让它
14 //退出进程让它变成sigcb
15 alarm(nsec);//ns之后的信号
16 pause();//暂停,永久阻塞,是被3秒之后的信号打断了
W> 17 }
18 int main(){
int main(){
19 signal(SIGQUIT,sigcb2);
20 mysleep(3);
21 printf("--------\n");
22 return 0;
23 }
pause永久阻塞,是被3秒之后的信号打断了
alarm和pause并非原子操作如果在这之间又出现
如果在alarm之后没来得及执行pause之前,来了一个SIGQUIT信号(不是原子操作),会调用sigcb2睡了10s钟意味着在这10秒之间的第三秒的时候定时器信号到来了等到10s运行完sigquit被处理完 从内核态返回用户态在返回之前检测有没信号要处理发现有个SIGALARM信号要处理,所以会顺便把SIGALARM也处理了等它返回用户态运行的时候定时器时间已经过了SIGALARM已经处理,此时pause会陷入一个永久睡眠.
mysleep的实现是:首先alarm(3)睡了3秒然后有个pause()永久暂停,因为alarm和pause不是原子性操作,这个时候当执行alarm之后突然来了一个信号SIGQUIT,对其进行了自定义处理方式,处理方式为sigcb,在sigcb当中sleep(10)睡了10秒钟意味着这个时候在没有执行pause之前就去执行sigcb 了,一共需要10s但是在第三秒的时候来了一个信号SIGALARM,(3s后定时器时间到了操作系统发送了一个SIGALARM信号),这时候需要把sigbc2处理完毕才可以,当10秒钟处理完毕之后返回用户态但是在返回之前发现有个SIGALRM信号要处理所以又去处理SIGALARM了,这个处理完之后返回用户态,这时候SIGALRM已经处理过了,这时候陷入暂停没有人唤醒了
如何解决:信号在返回的时候发现SIGALRM不能被处理,必须等返回调用pause之后再处理
#include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include<unistd.h>
W> 5 void sigcb2(int signo){
6 sleep(10);
7 }
8
W> 9 void sigcb(int signo){
10 //什么都不做,进程不会退出
11 }
12 int mysleep(int nsec){
13 signal(SIGALRM,sigcb);//重新定义改变SIGALRM的处理方式不让它
14 //退出进程让它变成sigcb
15 //此处用sigaction合适,最后还得还原SIGALRM原来的处理方式
16 sigset_t set,old;
17 sigemptyset(&set);
18 sigprocmask(SIG_BLOCK,&set,&old);//阻塞了SIGALRM
19 alarm(nsec);//ns之后的信号
20 // sigprocmask(SIG_UNBLOCK,&set,&old);//接触阻塞此处sigprocmask
//和pause也不是原子操作,需要的是接触SIGALRM的阻塞以及陷入暂停
22 //int sigsuspend(const sigset_t *mask);
23 //临时拿marsk中的信号阻塞信号然后陷入休眠,被唤醒之后阻塞集合还原
24 sigset_t mask;
25 //法1
26 sigfillset(&mask);
27 sigdelset(&mask,SIGALRM);//只把SIGALRM移除了,阻塞了所有信号但是没有
28 //阻塞SIGALRM,意味着只有SIGALRM可以唤醒,也就是除了SIGALRM
29 //阻塞所有的信号,陷入休眠
30 //法二sigemptyset(&mask);//清空mask,没有有阻塞任何信号,等价于法1
31 sigsuspend(&mask);//不包含SIGALRAM对SIGALRM解除了阻塞,
32 sigprocmask(SIG_SETMASK,&old,NULL);//还原其他的阻塞信号
33 signal(SIGALRM,SIG_DFL);//还原SIGALRM原来默认方式
W> 34 }
35 int main(){
36 signal(SIGQUIT,sigcb2);
37 mysleep(3);
38 printf("--------\n");
39 return 0;
40 }
gcc -o2 vol.c -o vol
-O2代码优化把i的值直接放在寄存器上面,而不会从内存获取,i改为0只是把内存当中的值改为0但是因为变量已经优化到寄存器上 面去了意味并不会和内存交互数据相当于cpu判断的时候不知道值变成0 了一直用1,代码优化的时候注意上面变量怎么修饰它,如何去处理,代码优化出现的逻辑问题有很多一定要注意.
关键字volatile
volatile int i=1;//保持内存可见性
,每次操作变量都需要重新从内存中获取
功能:防止编译器过度优化