网创优客建站品牌官网
为成都网站建设公司企业提供高品质网站建设
热线:028-86922220
成都专业网站建设公司

定制建站费用3500元

符合中小企业对网站设计、功能常规化式的企业展示型网站建设

成都品牌网站建设

品牌网站建设费用6000元

本套餐主要针对企业品牌型网站、中高端设计、前端互动体验...

成都商城网站建设

商城网站建设费用8000元

商城网站建设因基本功能的需求不同费用上面也有很大的差别...

成都微信网站建设

手机微信网站建站3000元

手机微信网站开发、微信官网、微信商城网站...

建站知识

当前位置:首页 > 建站知识

volatile关键字,竟态条件

volatile:防止编译器性能优化,与移植性有关。

在柘荣等地区,都构建了全面的区域性战略布局,加强发展的系统性、市场前瞻性、产品创新能力,以专注、极致的服务理念,为客户提供网站建设、成都做网站 网站设计制作按需求定制网站,公司网站建设,企业网站建设,成都品牌网站建设,成都营销网站建设,成都外贸网站建设,柘荣网站建设费用合理。

#include
#include
int done=0;
void handle(int sig)
{
    printf("get sig %d\n",sig);
    done=1;
}
int main()
{
    signal(SIGINT,handle);
    while(!done);
}

Makefile:

my_volatile:my_volatile.c

     gcc -o $@ $^  -O3

.PHONY:clean

clean:

     rm -f my_volatile

在while循环中:没有写入修改done的值,编译器会在读取变量的时候,将其从内存拿出存入寄存器并比较,在下次读取时,直接从寄存器中取该变量的值。 

而在信号处理函数中,有修改done的值,需写回内存。

可指定编译器优化级别

编译器优化级别:

  1. gcc -o my_volatile  my_volatile.c -O0//不优化

  2. gcc -o my_volatile  my_volatile.c -O1//默认

  3. gcc -o my_volatile  my_volatile.c -O2

  4. gcc -o my_volatile  my_volatile.c -O3//优化级别最高

此时会发生内存级别的不一致:寄存器:0,内存:1.

运行结果:

volatile关键字,竟态条件

要改变此程序显示预期效果,只需定义done为:volatile int done=0;//这样每次使用done时都会从内存中取done的值。

sig_atomic_t:由C语言提供。

虽然C代码只有一行,但是在32位机上对一个64位的long long变量赋值需要两条指令完成,因此不是原子操作。同样地,读取这个变量到寄存器需要两个32位寄存器才放得下,也需要两条指令, 不是原子操作。

为了解决这些平台相关的问题,C标准定义了一个类型sig_atomic_t,在不同平台的C语言库中取不同的类型,例如在32位机 上定义sig_atomic_t为int类型。

竟态条件:

由于异步事件在任何时候都有可能发生(这里的异步事件指出现更优 先级的进程),如果我们写程序时考虑不周密,就可能由于时序问题而导致错误,这叫做竞态条件 (Race Condition)。

#include
#include
#include
void handler(int sig)
{
    //do nothing
}
int my_sleep(int time)
{
    struct sigaction act;
    act.sa_handler=handler;
    act.sa_flags=0;
    sigemptyset(&act.sa_mask);
    struct sigaction old;
    memset(&old,'\0',sizeof(old));
    sigaction(SIGALRM,&act,&old);//注册信号处理函数
    alarm(time);//time秒后让系统发SIGALRM信号
    pause();//内核切换到别的进程运行
    int ret=alarm(0);
    sigaction(SIGALRM,&old,NULL);//恢复默认信号处理动作
    return ret;
}
int main()
{
    while(1)
    {
        printf("I am sleep...\n");
        my_sleep(5);
    }
    return 0;
}

虽然alarm(nsecs)紧接着的下一行就是pause(),但是无法保证pause()一定会在调用alarm(nsecs)之 后的nsecs秒之内被调用。

在调用pause之前屏蔽SIGALRM信号使它不能提前递达就可以了。
1. 屏蔽SIGALRM信号;
2. alarm(nsecs);
3. 解除对SIGALRM信号的屏蔽;
4. pause();
从解除信号屏蔽到调用pause之间存在间隙,SIGALRM仍有可能在这个间隙递达。

要是“解除信号屏蔽”和“挂起等待信号”这两步能合并成一个原子操作就好了,这正是sigsuspend
函数的功 能。 sigsuspend包含了pause的挂起等待功能,同时解决了竞态条件的问题,在对
时序要求严格的场合下都应该调用sigsuspend不是pause。

注:调用sigsuspend时,进程的信号屏蔽字由sigmask参数指定,可以通过指定sigmask来临时
解除对某 个信号的屏蔽,然后挂起等待,当sigsuspend返回时,进程的信号屏蔽字恢复为原
来的值,如果原来对该信号是屏蔽的,从sigsuspend返回后仍然是屏蔽的。

#include
#include
void handle(int sig)
{
    //do nothing
}
int sleep(int time)
{
    struct sigaction oldact,newact;
    sigset_t newmask,oldmask,suspmask;
    newact.sa_handler=handle;
    newact.sa_flags=0;
    sigemptyset(&newact.sa_mask);
    sigaction(SIGALRM,&newact,&oldact);//注册SIGALRM的信号处理函数
    sigemptyset(&newmask);
    sigaddset(&newmask,SIGALRM);
    sigprocmask(SIG_BLOCK,&newmask,&oldmask);//屏蔽SIGALRM信号;
    alarm(time);
    suspmask=oldmask;
    sigdelset(&suspmask,SIGALRM);//解除suspmask中SIGALRM信号的屏蔽;
    sigsuspend(&suspmask);//用suspmask去替换PCB中的block表,从而解除对SIGALRM信号的阻塞
    int ret=alarm(0);
    sigaction(SIGALRM,&oldact,NULL);
    sigprocmask(SIG_SETMASK,&oldmask,NULL);//恢复之前的系统默认处理信号方式
    return ret;
}
int main()
{
    while(1)
    {
        printf("I am sleep\n");
        sleep(5);
    }
    return 0;
}

子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子子进程了,子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。

优点:没花费时间在等待上,直到收到信号(异步信号)

#include
#include
#include
#include
void my_sigchld(int sig)
{
    int status=0;
    pid_t ret=waitpid(-1,&status,0);
    if(ret>0)
    {
        printf("sig: %d,code: %d\n",status&0xff,(status>>8)&0xff);
    }
}
int main()
{
    pid_t tid=fork();
    if(tid<0)
    {
        perror("fork");
        exit(1);
    }
    else if(tid==0)
    {
         sleep(10);//保证父进程已注册完信号处理函数,父,子进程谁先运行不确定
         printf("child is quit!\n");
         exit(1);
    }
    else
    {
         signal(SIGCHLD,my_sigchld);
         while(1);
    }
    return 0;
}

但是,如果一个父进程有100个子进程,收到好多SIGCHLD信号,只会保存一份,只能wait一份,故应该修改代码防止此情况发生

void my_sigchld(int sig)
{
    int status=0;
    pid_t ret;
    while((ret=waitpid(-1,&status,0))>0)
    {
        printf("sig: %d,code: %d\n",status&0xff,(status>>8)&0xff);
    }
 }

分享题目:volatile关键字,竟态条件
地址分享:http://bjjierui.cn/article/gppeds.html

其他资讯