免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 828 | 回复: 0
打印 上一主题 下一主题

关于volatile和(*(unsigned long *)&jiffies)++(转载) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-09-24 08:46 |只看该作者 |倒序浏览

volatile中文的意思是易于挥发的。在C语言中,如果一个变量被声明为volatile,则说明这个变量每回都要从内存读到寄存器中,操作完以后,
再将值写回到内存中,编译器(例如gcc)并不将值cache在寄存器中。对于多线程或SMP系统,要注意使用volatile。
    int wait = TRUE;
    thread1()                        thread2()
   {                                 {
       while (wait) {                     wait = FALSE;
           sleep(1000);                   ...
       }                             }
    }
当编译器看到thread1调用sleep的时候,它认为sleep是一个外部模块的函数,不可能改变wait的值,所以它"可能"就进行代码优化,将
wait的值cache在寄存器中,以提高效率。假如另外一个线程thread2开始运行,将wait改成FALSE,如果CPU没有预测到wait变量
的依赖关系的话,thread1就会一直sleep下去。在这种情况下,volatile可以强制编译器不将wait缓存到寄存器中。注意,一般情况下,
CPU是能检测到wait被改过了,从而将寄存器刷新,但是这和CPU的体系结构有关,Linux是能支持多种体系结构的,所以经常使用
volatile。
    有网友问为什么在do_timer()函数中,有一句(*(unsigned long *)&jiffies)++,为什么不直接用jiffies++呢?   这个问题和两个原因有关:      
    * jiffiers是volatile变量      
    * CPU可能会重新排序指令      
我编了3个小程序来测试,
    jiffier1.c      
    int main()      
    {         
        unsigned long jiffiers = 777;         
        jiffiers++;         
        return 0;      
    }

     gcc -S jiffer1.c,生成的汇编为
    .file   "jiffier1.c"         
    .version        "01.01"      
    gcc2_compiled.:      
    .text         
    .align 4      
.globl main         
    .type    main,@function      
main:         
    pushl   %ebp         
    movl    %esp, %ebp         
    subl    , %esp         
    movl    7, -4(%ebp)         
    leal    -4(%ebp), %eax         
    incl    (%eax)         
    movl    , %eax         
    leave         
    ret      
.Lfe1:         
    .size    main,.Lfe1-main         
    .ident  "GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.3 2.96-110)"
     从汇编中,可以看到jiffiers的值是直接在内存里加一的。
     jiffier2.c      
    int main()     
    {         
        volatile unsigned long jiffiers = 777;         
        jiffiers++;         
        return 0;     
    }
    生成的汇编为

    .file   "jiffier2.c"         
    .version        "01.01"     gcc2_compiled.:     
    .text         
    .align 4     
.globl main         
    .type    main,@function     
main:         
    pushl   %ebp         
    movl    %esp, %ebp         
    subl    , %esp         
    movl    7, -4(%ebp)         
    movl    -4(%ebp), %eax         
    incl    %eax         
    movl    %eax, -4(%ebp)         
    movl    , %eax         
    leave         
    ret     
.Lfe1:         
    .size    main,.Lfe1-main         
    .ident  "GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.3 2.96-110)"

    当jiffiers被声明为volatile时,在加一之前,会先从内存中将值读到寄存器%eax中, 直接都寄存器操作,完了以后不做cache,将值写回内存。它需要3条指令才完成了加一 操作,
        movl    -4(%ebp), %eax         
        incl    %eax         
        movl    %eax, -4(%ebp)
    也就是说,jiffiers++并不是原子操作,在多处理器环境中,
问题就出来了,jiffers有可能被加了两次,内存中却只是加了1。
另外的原因是这3条指令执行的时候,可能会被重新排序,从而破坏了操作的原子性。
当然我们也可以直接调用汇编指令加一,但是do_timers函数是独立于体系结构的,
所以Linux使用的一种最简单的方法,也就是(*(unsigned long *)&jiffiers)++
来解决这些问题。
    jiffier3.c
    int main()
    {
        volatile unsigned long jiffiers = 777;
        (*(unsigned long *)&jiffiers)++;
        return 0;
    }
    生成的汇编为
    .file   "jiffier3.c"         
    .version        "01.01"   gcc2_compiled.:     
    .text         
    .align 4     
.globl main         
    .type    main,@function     
main:         
    pushl   %ebp         
    movl    %esp, %ebp         
    subl    , %esp         
    movl    7, -4(%ebp)         
    leal    -4(%ebp), %eax         
    incl    (%eax)         
    movl    , %eax         
    leave         
    ret     
.Lfe1:         .size    main,.Lfe1-main         
.ident  "GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.3 2.96-110)"
可以看出来,生成的指令和jiffer1.c的一模一样,也就是说, (*(unsigned long
*)&jiffiers)++将volatile的jiffiers转换成一般的内存变量,
避免了用寄存器做cache,从而保证了jiffiers加一操作的原子性。
    Linux一开始也是直接用jiffiers++的,到后来的版本才改成(*(unsigned long *)&jiffiers)++, 可想而知写一个牢固的OS是不容易的,要解决的隐含细节非常多。     
    Good Luck, Linux!
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/11176/showart_388718.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP