volatile实例

来源:互联网 发布:windows snmp工具 编辑:程序博客网 时间:2024/06/05 14:31
volatile实例

一、volatile的汇编解释
main.c:
#include <stdio.h>int a = 100;void main(){    while(a == 100){}    printf("finally...\n");}



优化要开得比较高,gcc -O2 -S main.c得到
    cmpl    $100, a //先比较a和100
    je  .L4    //相等就跳到L4
..........
    ret
.L4:
    jmp .L4    //L4再跳到L4,就是把和a比较的部分给删了,来了死循环,因为编译器认定了a不会变
..........
    .data
    .align 4
    .type   a, @object
    .size   a, 4
a:
    .long   100

加上了volatile int a = 100就变成了:
.L3:
    movl    a, %eax
    cmpl    $100, %eax
    je  .L3
也是个一直从L3调回L3的过程,但是L3本身包括了对a是否等于100的验证。
总之,volatile至少一点效果是告诉编译器,这个变量别去乱优化,不像你想象的那样。

二、volatile实用举例
对于一中的int a,怎么都想不到要变的方法。改一下就可以了:
#include <stdio.h>#include <signal.h>int a = 100;void handler(int num){    a = 200;}void main(){    signal(SIGUSR1,handler);    while(a == 100){}    printf("finally...\n");}


gcc -O2 main.c之后执行,kill -SIGUSR1 进程号是没有用的,就像之前说的已经优化成死循环了,改成volatile int a就可以了。

另一个简单的共享内存例子:
//仅作为演示用,异常情况未作处理#include <sys/shm.h>#include <stdio.h>int main(){    int shmid = shmget(1234,sizeof(char),0666|IPC_CREAT);    void *shm = shmat(shmid,NULL,0);    char *pc = shm;    *pc = 1;    while(*pc){}    printf("finally");    shmdt(shm);}


汇编代码为:
........
    movb    $1, (%eax)
.L2:
    jmp .L2        //有是自己跳回自己,根本就没有去看*pc的值
........
改下代码得到个反例:
//仅作为演示用,异常情况未作处理#include <sys/shm.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>int main(){    int shmid = shmget(1234,sizeof(char),0666|IPC_CREAT); //共享内存相关函数不多,用法也方便,不熟悉百度也能两下上手    if(fork() == 0){    //子进程        sleep(10);    //休息十秒好看打印结果        void *shm = shmat(shmid,NULL,0); //子进程能从副进程出继承到变量的值,并且往往是写时才复制        char *pc = shm;        *pc = 0;        printf("son:done\n");    //改完了,打印done        shmdt(shm);        exit(0);    }    void *shm = shmat(shmid,NULL,0);    //不在这之后fork是shm返回的线性地址空间子进程和父进程完全是两码事    char *pc = shm;    //volatile char *pc = shm;    *pc = 1;    while(*pc){}    printf("finally");    shmdt(shm);}

像预想的那样,非volatile的-O2完全就优化成了死循环,而volatile的则在子进程打印后父进程打印(当然具体退出时间前后不一定)。
总之,编译器往往“聪明反被聪明误”,像上面的a和*pc都是对程序员可预期会变的,但是编译器看不出来,这时就要用volatile明确地告知。
原创粉丝点击