Linux驱动中的outb()与volatile


在Linux的驱动程序中,都会使用大量的outb、outw、inb、inw等等宏来访问硬件或寄存器。这些宏的定义都在相应处理器体系下的include\asm目录下的io.h中定义。追究下去,这些宏最终就是一个volatile变量的的赋值:

#define __arch_putb(v,a)      (*(volatile unsigned char *)(a) = (v))

#define __raw_writeb(v,a)    __arch_putb(v,a)

#define outb(v,p)                 __raw_writeb(v,__io(p))

在(*(volatile unsigned char *)(a) = (v))中,a是一个物理地址(实地址,多数是特殊功能寄存器地址)。(volatile unsigned char *)对a进行类型转换,成为一个指向该地址指针,最后*(volatile unsigned char *)(a)引用该指针对该地址赋值v。这样就可以达到访问底层硬件的目的了。

应用程序中定义的变量都是经过优化的,即在运行的时候变量会暂存在CPU的Cache中。CPU修改该变量都是修改Cache中的值,而不会更新内存中的值。直到任务的切换或Cache失效,Cache中的值才会更新到内存中。这样访问硬件是没有时效性的,即有可能程序对硬件操作了多次后,实际才对硬件操作一次。www.bkjia.com而驱动程序必须要求每次操作都要对硬件起作用。所以,如果要访问硬件就必须避免编译器对其操作进行优化,使得CPU对硬件的操作不经过Cache而直接访问目标。用volatile声明一个变量就可以达到这个目的。     

volatile关键字是一种类型修饰符,用它声明的变量表示可能被未知的因素(如:硬件等)更改。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。     

(*(volatile unsigned char *)(a) = (v))访问的并不一定是实际的物理地址,可能是经过内存管理重新映射后的地址,也就是虚拟地址。只不过volatile是使变量访问不经过Cache优化。

相关内容