~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/Documentation/translations/zh_CN/process/volatile-considered-harmful.rst

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 .. _cn_volatile_considered_harmful:
  2 
  3 .. include:: ../disclaimer-zh_CN.rst
  4 
  5 :Original: :ref:`Documentation/process/volatile-considered-harmful.rst
  6            <volatile_considered_harmful>`
  7 
  8 如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
  9 交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
 10 译存在问题,请联系中文版维护者::
 11 
 12         英文版维护者: Jonathan Corbet <corbet@lwn.net>
 13         中文版维护者: 伍鹏  Bryan Wu <bryan.wu@analog.com>
 14         中文版翻译者: 伍鹏  Bryan Wu <bryan.wu@analog.com>
 15         中文版校译者: 张汉辉  Eugene Teo <eugeneteo@kernel.sg>
 16                        杨瑞  Dave Young <hidave.darkstar@gmail.com>
 17                        时奎亮 Alex Shi <alex.shi@linux.alibaba.com>
 18 
 19 为什么不应该使用“volatile”类型
 20 ==============================
 21 
 22 C程序员通常认为volatile表示某个变量可以在当前执行的线程之外被改变;因此,在内核
 23 中用到共享数据结构时,常常会有C程序员喜欢使用volatile这类变量。换句话说,他们经
 24 常会把volatile类型看成某种简易的原子变量,当然它们不是。在内核中使用volatile几
 25 乎总是错误的;本文档将解释为什么这样。
 26 
 27 理解volatile的关键是知道它的目的是用来消除优化,实际上很少有人真正需要这样的应
 28 用。在内核中,程序员必须防止意外的并发访问破坏共享的数据结构,这其实是一个完全
 29 不同的任务。用来防止意外并发访问的保护措施,可以更加高效的避免大多数优化相关的
 30 问题。
 31 
 32 像volatile一样,内核提供了很多原语来保证并发访问时的数据安全(自旋锁, 互斥量,内
 33 存屏障等等),同样可以防止意外的优化。如果可以正确使用这些内核原语,那么就没有
 34 必要再使用volatile。如果仍然必须使用volatile,那么几乎可以肯定在代码的某处有一
 35 个bug。在正确设计的内核代码中,volatile能带来的仅仅是使事情变慢。
 36 
 37 思考一下这段典型的内核代码::
 38 
 39     spin_lock(&the_lock);
 40     do_something_on(&shared_data);
 41     do_something_else_with(&shared_data);
 42     spin_unlock(&the_lock);
 43 
 44 如果所有的代码都遵循加锁规则,当持有the_lock的时候,不可能意外的改变shared_data的
 45 值。任何可能访问该数据的其他代码都会在这个锁上等待。自旋锁原语跟内存屏障一样—— 它
 46 们显式的用来书写成这样 —— 意味着数据访问不会跨越它们而被优化。所以本来编译器认为
 47 它知道在shared_data里面将有什么,但是因为spin_lock()调用跟内存屏障一样,会强制编
 48 译器忘记它所知道的一切。那么在访问这些数据时不会有优化的问题。
 49 
 50 如果shared_data被声名为volatile,锁操作将仍然是必须的。就算我们知道没有其他人正在
 51 使用它,编译器也将被阻止优化对临界区内shared_data的访问。在锁有效的同时,
 52 shared_data不是volatile的。在处理共享数据的时候,适当的锁操作可以不再需要
 53 volatile —— 并且是有潜在危害的。
 54 
 55 volatile的存储类型最初是为那些内存映射的I/O寄存器而定义。在内核里,寄存器访问也应
 56 该被锁保护,但是人们也不希望编译器“优化”临界区内的寄存器访问。内核里I/O的内存访问
 57 是通过访问函数完成的;不赞成通过指针对I/O内存的直接访问,并且不是在所有体系架构上
 58 都能工作。那些访问函数正是为了防止意外优化而写的,因此,再说一次,volatile类型不
 59 是必需的。
 60 
 61 另一种引起用户可能使用volatile的情况是当处理器正忙着等待一个变量的值。正确执行一
 62 个忙等待的方法是::
 63 
 64     while (my_variable != what_i_want)
 65         cpu_relax();
 66 
 67 cpu_relax()调用会降低CPU的能量消耗或者让位于超线程双处理器;它也作为内存屏障一样出
 68 现,所以,再一次,volatile不是必需的。当然,忙等待一开始就是一种反常规的做法。
 69 
 70 在内核中,一些稀少的情况下volatile仍然是有意义的:
 71 
 72   - 在一些体系架构的系统上,允许直接的I/0内存访问,那么前面提到的访问函数可以使用
 73     volatile。基本上,每一个访问函数调用它自己都是一个小的临界区域并且保证了按照
 74     程序员期望的那样发生访问操作。
 75 
 76   - 某些会改变内存的内联汇编代码虽然没有什么其他明显的附作用,但是有被GCC删除的可
 77     能性。在汇编声明中加上volatile关键字可以防止这种删除操作。
 78 
 79   - Jiffies变量是一种特殊情况,虽然每次引用它的时候都可以有不同的值,但读jiffies
 80     变量时不需要任何特殊的加锁保护。所以jiffies变量可以使用volatile,但是不赞成
 81     其他跟jiffies相同类型变量使用volatile。Jiffies被认为是一种“愚蠢的遗留物"
 82     (Linus的话)因为解决这个问题比保持现状要麻烦的多。
 83 
 84   - 由于某些I/0设备可能会修改连续一致的内存,所以有时,指向连续一致内存的数据结构
 85     的指针需要正确的使用volatile。网络适配器使用的环状缓存区正是这类情形的一个例
 86     子,其中适配器用改变指针来表示哪些描述符已经处理过了。
 87 
 88 对于大多代码,上述几种可以使用volatile的情况都不适用。所以,使用volatile是一种
 89 bug并且需要对这样的代码额外仔细检查。那些试图使用volatile的开发人员需要退一步想想
 90 他们真正想实现的是什么。
 91 
 92 非常欢迎删除volatile变量的补丁 - 只要证明这些补丁完整的考虑了并发问题。
 93 
 94 注释
 95 ----
 96 
 97 [1] https://lwn.net/Articles/233481/
 98 [2] https://lwn.net/Articles/233482/
 99 
100 致谢
101 ----
102 
103 最初由Randy Dunlap推动并作初步研究
104 由Jonathan Corbet撰写
105 参考Satyam Sharma,Johannes Stezenbach,Jesper Juhl,Heikki Orsila,
106 H. Peter Anvin,Philipp Hahn和Stefan Richter的意见改善了本档。

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php