1、I/O 端口和 I/O 内存
每个外设都是通过读写其寄存器来控制的。通常一个设备有几个寄存器,它们位于内存地址空间或者I/O地址空间,并且地址是连续的。
在硬件层上,内存区和I/O区域没有概念上的区别: 它们都是通过在地址总线和控制总线上发出电信号来存取(即,读写信号),并且通过数据总线来读写数据。
在一些CPU制造商在其芯片上实现了一个单地址空间(统一编址)的同时,其它的CPU制造商认为外设不同于内存,应该有一个独立的地址空间给外设(单独编址),其生产处理器(特别是x86家族)的I/O端口有自己的读写信号线和特殊的CPU指令来存取端口。因为外设要与外设总线相匹配,并且大部分流行的I/O总线都是以个人计算机(主要是x86家族)作为模型,所以即便那些没有单独地址空间给I/O端口的处理器,也必须在访问外设时模拟成读写端口。这通常通过外部芯片组(PC中的南北桥)或者在CPU核中附加额外电路来实现(基于嵌入式应用的处理器)。关于编址方式、I/O 端口和 I/O 内存更详细内容见http://blog.chinaunix.net/u3/96613/showart_1926286.html
由于同样的理由,Linux在所有计算机平台上都实现了I/O端口,甚至在那些单地址空间的CPU平台上(模拟I/O端口)。但并不是所有的设备都会将其寄存器映射到I/O端口。虽然ISA设备普遍使用I/O端口,但大部分PCI设备将寄存器映射到某个内存地址区。这种I/O内存方法通常是首选的,因为它无需使用特殊的处理器指令,CPU存取内存也更有效率,并且编译器在存取内存时在寄存器分配和寻址模式的选择上有更多自由。
1.1、I/O 寄存器和常规内存
I/O寄存器和RAM的主要不同是I/O操作有边际效应(side effect),而内存操作没有:访问内存只是在内存某一位置存储数值。因为内存存取速度严重影响CPU的性能,编译器可能会对源码进行优化,主要是:使用高速缓存和重排读/写指令的顺序。对于传统内存(至少在单处理器系统)这些优化是透明有益的,但是对于I/O 寄存器,这可能是致命错误,因为它们干扰了那些"边际效应"(驱动程序存取I/O 寄存器就是为了获取边际效应)。因此,驱动程序必须确保在存取寄存器时,不能使用高速缓存并且不能重新编排读写指令的顺序。
side effect 是指:访问I/O寄存器时,不仅仅会像访问普通内存一样影响存储单元的值,更重要的是它可能改变CPU的I/O端口电平、输出时序或CPU对I/O端口电平的反应等等,从而实现CPU的控制功能。CPU在电路中的意义就是实现其side effect 。举个例子,有些设备的中断状态寄存器只要一读取,便自动清零。 |
硬件缓冲的问题是最易解决的:只要将底层硬件配置(或者自动地或者通过Linux 初始化代码)为当存取I/O区时,禁止任何硬件缓冲(不管是I/O 内存还是I/O 端口)。
编译器优化和硬件重编排读写指令顺序的解决方法是:在硬件或处理器必须以一个特定顺序执行的操作之间安放一个内存屏障(memory barrier)。Linux 提供以下几个宏来实现这个功能:
#include <linux/kernel.h> |
文章评论(0条评论)
登录后参与讨论