Linux源码研究-内核开发-volatile类型

C程序员通常认为volatile表示某个变量可以在当前执行的线程外被改变,因此在内核中用到共享数据结构时,常常会有C程序员使用volatile这类变量,然而在内核中使用volatile几乎总是错误的。

理解volatile的关键是知道它的目的用来消除优化,在内核中,程序员必须防止意外的并发访问破坏共享的数据结构,这其实是一个完全不同的任务。

像volatile一样,内核提供了很多原语来保证并发访问时的数据安全(自旋锁、互斥量、内存屏障等),同样也可以防止意外的优化。如果可以正确的使用这些内核原语,那么就没有必要再使用volatile。在正确的内核代码中,volatile能带来的仅仅是使事情变慢。

思考一下这段典型的内核代码

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

如果所有的代码都遵循加锁规则,当持有the_lock的时候,不可能意外的改变shared_data的值。任何可能访问该数据的其他代码都会在这个锁上等待。自旋锁原语和内存屏障一样,它们显式的用来书写这样,意味着数据访问不会跨越它们页被优化。所以本来编译器认为它知道在shared_data里面将有什么,但是因为spin_lock()调用跟内存屏障一样,会强制编译器忘记它所知道的一切,那么在访问这些数据时不会有优你给的问题。

volatile的存储类型最初是为那些内存映射的I/O寄存器而定义。在内核里,寄存器访问也应该被锁保护,但是人们也不希望编译器“优化”临界区内的寄存器访问。内核里的I/O的内存访问是通过访问函数完成的,不赞成通过指针对I/O内存的直接访问,并且不是所有体系架构上都能工作。那些访问函数正是为了防止意外优化而写的,因些,再说一次,volatile类型不是必须的。

在内核中,一些稀少的情况下volatile仍然有意义的:

  • 在一些体系架构的系统上,允许直接的I/O内存访问,那么前面提到访问函数可以使用volatile。
  • 某些会改变内存的内联汇编代码虽然没有什么其他明显的附作用,理是有被GCC删除的可能性。在汇编声明中加上volatile关键字可以防止这种删除操作。
  • Jiffies变量是一种特殊情况,虽然每次引用它的时候都可以有不同的值,但读jiffies变量时不需要任何特殊的加锁保护。所以jiffies变量可以使用volatile,但不赞成其他跟jiffies相同类型变量使用volatile。jiffies被认为是一种“愚蠢的遗留物”,因为解决这个问题比保持现状要麻烦的多。
  • 由于某些I/O设备可能会悠 连续一致的内存,所以有时,指向连续内存的数据结构的指针需要正确的使用volatile。网络适配器使用的环状缓存区正是这类情形的一个例子,其中适配器用改变指针来表示哪些描述符已经处理过了。

猜你喜欢

转载自blog.csdn.net/chs007chs/article/details/80222580