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中的值才会更新到内存中。这样访问硬件是没有时效性的,即有可能程序对硬件操作了多次后,实际才对硬件操作一次。而驱动程序必须要求每次操作都要对硬件起作用。所以,如果要访问硬件就必须避免编译器对其操作进行优化,使得CPU对硬件的操作不经过Cache而直接访问目标。用volatile声明一个变量就可以达到这个目的。
      volatile关键字是一种类型修饰符,用它声明的变量表示可能被未知的因素(如:硬件等)更改。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
 
      补充、更正:(*(volatile unsigned char *)(a) = (v))访问的并不一定是实际的物理地址,可能是经过内存管理重新映射后的地址,也就是虚拟地址。只不过volatile是使变量访问不经过Cache优化。
   在内核可访问内存空间和设备本身之间传输数据则要视不同的计算机而定。一些计算机需要使用一些特殊CPU输入输出指令来完成这项工作,这通常被称为DMA(直接内存访问)。而另一种方案则是使用内存映射I/O来解决,通常使用系统提供的I/O函数,比如inb()和outb()来分别地从I/O地址(即端口)读取和向I/O地址输出一单字节,可以使用这些宏。
      说白了,在ARM这样的体系结构里,IO空间和Memery空间统一的情况下,可以在驱动中直接 对地址进行操作,不采用这套宏
补充知识:
几乎每一种外设都是通过读写设备上的寄存器来进行的。外设寄存器也称为“I/O端口”,通常包括:控制寄存器、状态寄存器和数据寄存器三大类,而且一个外设的寄存器通常被连续地编址。CPU对外设IO端口物理地址的编址方式有两种:一种是I/O映射方式(I/O-mapped),另一种是内存映射方式(Memory-mapped)。而具体采用哪一种则取决于CPU的体系结构。

  有些体系结构的CPU(如,PowerPC、m68k等)通常只实现一个物理地址空间(RAM)。在这种情况下,外设I/O端口的物理地址就被映射到CPU的单一物理地址空间中,而成为内存的一部分。此时,CPU可以象访问一个内存单元那样访问外设I/O端口,而不需要设立专门的外设I/O指令。这就是所谓的“内存映射方式”(Memory-mapped)。

  而另外一些体系结构的CPU(典型地如X86)则为外设专门实现了一个单独地地址空间,称为“I/O地址空间”或者“I/O端口空间”。这是一个与CPU地RAM物理地址空间不同的地址空间,所有外设的I/O端口均在这一空间中进行编址。CPU通过设立专门的I/O指令(如X86的IN和OUT指令)来访问这一空间中的地址单元(也即I/O端口)。这就是所谓的“I/O映射方式”(I/O-mapped)。与RAM物理地址空间相比,I/O地址空间通常都比较小,如x86 CPU的I/O空间就只有64KB(0-0xffff)。这是“I/O映射方式”的一个主要缺点。
   
   “CPU的单一物理地址空间”就是指RAM,前文有描述"有些体系结构的CPU(如,PowerPC、m68k等)通常只实现一个物理地址空间(RAM)"。也就是说,这类结构(内存映射方式)的CPU只对RAM编址,其他对象如I/O,ROM等都要映射到RAM中才能被CPU访问。

     "而另外一些体系结构的CPU(典型地如X86)则为外设专门实现了一个单独地地址空间",这类(I/O映射方式)CPU就是我们现在用的PC中的CPU,这类CPU的寻址空间不是分为常规内存/保留内存/扩展内存吗?其中的保留内存就是用来对I/O,ROM等的直接编址。但由于参与直接编址的I/O,ROM等的读的速度没有RAM快,所以主板提供了映射功能,映射后参与保留内存编址的实际也是物理RAM,这种情形下就跟内存映射方式类似了。

你可能感兴趣的:(outb函数与volatile 类型变量)