对于内存映射的I/O端口我们可以像内存读写一样与外设进行交互,但二者之间并不完全等同,请看如下的代码


#define DEVICE_READY 0x01
void device_activate(int * _port)
{
    *_port = DEVICE_READY;
    while (*_port != DEVICE_READY){
        ;
    }
}


device_activate函数的作用是激活某一外设,参数_port是外设的控制端口地址,通过向该寄存器的bit0写1的方式来激活它,外设准备好以后,控制端口的bit0将被外设置1,函数正是通过不断的查询该位来判断外设是否被初始化好了。请注意,外设的寄存器并不像内存那样,我们写1进去读出来的也一定是1,这完全取决于外设的行为。


device_activate函数的功能在不使用编译优化选项时是正常的,这可以从如下的反汇编程序看出


void device_activate(int * _port)
{
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 89 7d f8             mov    %rdi,-0x8(%rbp)
    *_port = DEVICE_READY;
   8:   48 8b 45 f8             mov    -0x8(%rbp),%rax
   c:   c7 00 01 00 00 00       movl   $0x1,(%rax)
    while (*_port != DEVICE_READY){
  12:   90                      nop
  13:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  17:   8b 00                   mov    (%rax),%eax
  19:   83 f8 01                cmp    $0x1,%eax
  1c:   75 f5                   jne    13 
        ;
    }
}


但是当使用编译优化选项时,它的功能就不正常了,反汇编如下


void device_activate(int * _port)
{
    *_port = DEVICE_READY;
   0:   c7 07 01 00 00 00       movl   $0x1,(%rdi)
    while (*_port != DEVICE_READY){
        ;
    }
}
   6:   c3                      retq


从汇编代码中可以看出函数中的while语句被优化掉了。这是因为编译器“聪明地”认为:将寄存器的bit0设置为1后读入的值也一定为1,所以那个while语句就是多余的了。为了防止这种情况的发生,我们要告诉编译器这是端口而不是内存,这就需要volatile关键字,使用volatile关键字更改后的代码如下


#define DEVICE_READY 0x01
void device_activate(volatile int * _port)
{
    *_port = DEVICE_READY;
    while (*_port != DEVICE_READY){
        ;
    }
}


再一次使用优化选项编译和反汇编的结果如下


void device_activate(volatile int * _port)
{
    *_port = DEVICE_READY;
   0:   c7 07 01 00 00 00       movl   $0x1,(%rdi)
   6:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
   d:   00 00 00
    while (*_port != DEVICE_READY){
  10:   8b 07                   mov    (%rdi),%eax
  12:   83 f8 01                cmp    $0x1,%eax
  15:   75 f9                   jne    10 
        ;
    }
}
  17:   f3 c3                   repz retq


这次编译器就没有优化掉while语句,所以在编写与外设打交道的程序时要注意运用volatile关键字


注:本文所用程序用例出自 李云的《专业嵌入式软件开发》