为什么 volatile 关键字不能在 kernel 中使用(linux kernel 文档翻译)

为什么 volatile 关键字不能在 kernel 中使用

    使用C语言的程序员以前总认为 volatile的意思是这个变量可能会在当前线程外的其他地方被改变(中断,其他device或CPU线程等)。因此他们有时会尝试在kernel中把votatile用于处理共享数据结构。换一种方式说,他们之前一直认为volatile是一种简单的原子操作方式,但这是错误的。在kernel中对volatile的使用基本上从来都是错误的:这篇文章将告诉你为什么。
    理解 volatile 的关键在于 volatile 的目的仅仅是抑制编译器优化,这可能并不是当你使用 volatile 时的真正意图。在kernel中,程序员必须保护共享数据结构不被unwanted concurrent access(并发访问)破坏。这是一个非常困难的工作。这一保护过程也会通过一种更为有效的方式避免编译器优化带来的问题。The process of protecting against unwanted concurrency will also avoid almost all optimization-related problems in a more efficient way.(总之就是说你处理的好根本用不着votatile的意思。)
    和volatile一样,kernel的原语是使共享数据结构的访问安全(如 spinlocks, mutexes, memory barriers 等),被设计成了阻止不需要的编译器优化,如果这些特点被正确的使用了,根本不需要再使用volatile。如果volatile在这种情况下仍然是必需的,很可能你的程序是有bug的。在 properly-written 的 kernel code中,volatile 只能拖慢效率。
    例如如下代码:

    spin_lock(&the_lock);
    do_something_on(&shared_data);
    do_something_else_with(&shared_data);
    spin_unlock(&the_lock);

    如果所有代码都遵循了锁原则,共享数据的值在锁持有期间是不会被改变的。任何想要使用该值的代码都必须等待锁被释放。spin_lock 原语起到了内存屏障的作用(它明显是被设计来起到这个作用的),所以数据访问是不可能被超越spinlock的界限而被优化的(data accesses will not be optimized across them)。因此编译器可能对共享内存内的值有自己的预期,并做出相应优化,但spin_lock(作为一种内存屏障),强迫编译器忘记他的预期值。因此对于这个共享数据是不会出现编译器优化问题的。
    就算共享数据被声明为 volatile,spin_lock 仍然是必需的。但是编译器也会被阻止优化任何对该数据的访问,包括在critical section 内,这是不必要的因为我们知道在critical section内时是不会有其他人使用该共享数据的。当持有锁时,该共享数据不具有 volatile属性(不会被非预期的更改)。所以当处理共享数据时,恰当的使用锁机制使得 volatile 不仅没用,而且有潜在的坏处。
    volatile 本来是为外部寄存器(memory-mapped I/O registers)使用的。在kennel内,寄存器的访问也必须用锁来保护,但是我们也不希望编译器优化critical section内的寄存器访问。但是,在kernel中外部IO寄存器的访问总是通过访问函数实现的,对IO内存映射直接用指针访问是不推荐的,而且也不适合于每个架构。访问函数阻止了不需要的优化,所以volatile也是不必要的。
    另一种情况,当处理器在忙等一个变量的值的时候。正确的做法是:

    while (my_variable != what_i_want)
    cpu_relax();
    
    cpu_relax() 可以降低CPU的耗电,或者让步给与它同CPU的另一个超线程hyperthread (yield to a
hyperthreaded twin processor),这也碰巧起到了编译屏障的作用,所以volatile还是不必要的,另外忙等本身也是一个非常不好的做法。


    还有一些不常用的情况,volatile 可以在kernel中使用:
    
    1.在上面提到的访问函数中可能会在确实支持直接访问外部IO内存的架构下使用volatile。本质上,每个访问函数内部都会变成一个小的critical section,确保访问如编程者所想的那样发生。
    2.需要改变某内存但不产生 side effects的内联汇编代码可能会被GCC的优化删除。加上volatile关键字是的该代码不会被删除。

    3.jiffies 的特殊之处是他可以在每次被引用的时候值都不同,但他又不需要任何锁就可以访问。所以jiffies 可以是 volatile 的。但是添加其他类似jiffies的强烈不推荐的。Jiffies 是一个愚蠢的历史遗留问题(Linus's words)。,但是解决这一问题所引起的问题有不值得这样做。

    4.指向连续内存的指针如果可能被 IO 设备修改,有时使用volatile是合理的。网络适配器的ring buffer中,适配器可能会更改指针表明那些描述符已经被处理过了,是其中一个例子。























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值