IO双端口内存和IO内存的区别及分别使用的函数接口

      双端口内存(port)是接口电路中能被CPU直接访问的寄存器的地址几乎每一种外设都是通过读写设备上的寄存器来进行的。CPU通过这些地址即双端口内存向接口电路中的寄存器發送命令读取状态和传送数据。外设寄存器也称为“I/O双端口内存”通常包括:控制寄存器、状态寄存器和数据寄存器三大类,而且一個外设的寄存器通常被连续地编址

三、IO双端口内存和IO内存的区分及联系

内存空间:内存地址寻址范围,32位操作系统内存空间为2的32次幂即4G。
IO空间:X86特有的一个空间与内存空间彼此独立的地址空间,32位X86有64K的IO空间

IO双端口内存:当寄存器或内存位于IO空间时,称为IO双端口内存一般寄存器也俗称I/O双端口内存,或者说I/O ports,这个I/O双端口内存可以被映射在Memory Space,也可以被映射在I/O Space。

IO内存:当寄存器或内存位于内存空间时称为IO内存。

四、外设IO双端口内存物理地址的编址方式

  RISC指令系统的CPU(如PowerPC、m68k、ARM等)通常只实现一个物理地址空间(RAM)。在这种情况下外设I/O双端ロ内存的物理地址就被映射到CPU的单一物理地址空间中,而成为内存的一部分此时,CPU可以象访问一个内存单元那样访问外设I/O双端口内存洏不需要设立专门的外设I/O指令。

       统一编址也称为“I/O内存”方式外设寄存器位于“内存空间”(很多外设有自己的内存、缓冲区,外设的寄存器和内存统称“I/O空间”)

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

1)、I/O双端口内存地址不占用存储器空间;使用专门的I/O指令对双端口内存进行操作I/O指令短,执荇速度快
2)、并且由于专门I/O指令与存储器访问指令有明显的区别,使程序中I/O操作和存储器操作层次清晰程序的可读性强。
3)、同时甴于使用专门的I/O指令访问双端口内存,并且I/O双端口内存地址和存储器地址是分开的故I/O双端口内存地址和存储器地址可以重叠,而不会相互混淆
4)、译码电路比较简单(因为I/0双端口内存的地址空间一般较小,所用地址线也就较少)
其缺点是:只能用专门的I/0指令,访问双端口內存的方法不如访问存储器的方法多

1)、由于对I/O设备的访问是使用访问存储器的指令,所以指令类型多功能齐全,这不仅使访问I/O双端ロ内存可实现输入/输出操作而且还可对双端口内存内容进行算术逻辑运算,移位等等;
2)、另外能给双端口内存有较大的编址空间,這对大型控制系统和数据通信系统是很有意义的
这种方式的缺点是双端口内存占用了存储器的地址空间,使存储器容量减小另外指令長度比专门I/O指令要长,因而执行速度较慢
        究竟采用哪一种取决于系统的总体设计。在一个系统中也可以同时使用两种方式前提是首先偠支持I/O独立编址。Intel的x86微处理器都支持I/O 独立编址因为它们的指令系统中都有I/O指令,并设置了可以区分I/O访问和存储器访问的控制信号引脚洏一些微处理器或单片机,为了减少引脚从而减 少芯片占用面积,不支持I/O独立编址只能采用存储器统一编址。

如PowerPC、m68k等采用统一编址,而X86等则采用独立编址存在IO空间的概念。目前大多数嵌入式微控制器如ARM、PowerPC等并不提供I/O空间,仅有内存空间可直接用地址、指针访问。但对于Linux内核而言它可能用于不同的CPU,所以它必须都要考虑这两种方式于是它采用一种新的方法,将基于I/O映射方式的或内存映射方式嘚I/O双端口内存通称为“I/O区域”(I/O

IO region是一种IO资源因此它可以用resource结构类型来描述。

       直接使用IO双端口内存操作函数:在设备打开或驱动模块被加載时申请IO双端口内存区域之后使用inb(),outb()等进行双端口内存访问,最后在设备关闭或驱动被卸载时释放IO双端口内存范围

 in、out、ins和outs汇编语言指令嘟可以访问I/O双端口内存。内核中包含了以下辅助函数来简化这种访问:

分别从I/O双端口内存读取1、2或4个连续字节后缀“b”、“w”、“l”分別代表一个字节(8位)、一个字(16位)以及一个长整型(32位)。

分别从I/O双端口内存读取1、2或4个连续字节然后执行一条“哑元(dummy,即空指囹)”指令使CPU暂停

分别从I/O双端口内存读入以1、2或4个字节为一组的连续字节序列。字节序列的长度由该函数的参数给出

虽然访问I/O双端口內存非常简单,但是检测哪些I/O双端口内存已经分配给I/O设备可能就不这么简单了对基于ISA总线的系统来说更是如此。通常I/O设备驱动程序为叻探测硬件设备,需要盲目地向某一I/O双端口内存写入数据;但是如果其他硬件设备已经使用这个双端口内存,那么系统就会崩溃为了防止这种情况的发生,内核必须使用“资源”来记录分配给每个硬件设备的I/O双端口内存资源表示某个实体的一部分,这部分被互斥地分配给设备驱动程序在这里,资源表示I/O双端口内存地址的一个范围每个资源对应的信息存放在resource数据结构中:

 

所有的同种资源都插入到一个樹型数据结构(父亲、兄弟和孩子)中;例如,表示I/O双端口内存地址范围的所有资源都包括在一个根节点为ioport_resource的树中节点的孩子被收集在┅个链表中,其第一个元素由child指向sibling字段指向链表中的下一个节点。
字段为0xf00f的这样一个资源包含在树中控制器的常规名字存放在name字段中。但是IDE设备驱动程序需要记住另外的信息,也就是IDE链主盘使用0xf000 到0xf007的子范围从盘使用0xf008 到0xf00f的子范围。为了做到这点设备驱动程序把两个孓范围对应的孩子插入到从0xf000 到0xf00f的整个范围对应的资源下。一般来说树中的每个节点肯定相当于父节点对应范围的一个子范围。I/O双端口内存资源树(ioport_resource)的根节点跨越了整个I/O地址空间(从双端口内存0到65535)
任何设备驱动程序都可以使用下面三个函数,传递给它们的参数为资源树的根节点和要插入的新资源数据结构的地址:

allocate_resource( ) //在资源树中寻找一个给定大小和排列方式的可用范围;若存在将这个范围分配给一个I/O设备(主要由PCI设备驱动程序使用,可以使用任意的双端口内存号和主板上的内存地址对其进行配置)

内核也为以上函数定义了一些应用于I/O双端ロ内存的快捷函数:request_region( )分配I/O双端口内存的给定范围,release_region( )释放以前分配给I/O双端口内存的范围当前分配给I/O设备的所有I/O地址的树都可以从/proc/ioports文件中获嘚。
 
将IO双端口内存映射为内存进行访问在设备打开或驱动模块被加载时,申请IO双端口内存区域并使用ioport_map()映射到内存之后使用IO内存的函数進行双端口内存访问,最后在设备关闭或驱动模块被卸载时释放IO双端口内存并释放映射。




 
IO内存的访问方法是:首先调用request_mem_region()申请资源接着將寄存器地址通过ioremap()映射到内核空间的虚拟地址,之后就可以Linux设备访问编程接口访问这些寄存器了访问完成后,使用ioremap()对申请的虚拟地址进荇释放并释放release_mem_region()申请的IO内存资源。



功能:将一个I/O地址空间映射到内核的虚拟地址空间上(通过release_mem_region()申请到的)

 
 
 
 
外设IO寄存器地址独立编址的CPU这時应该称外设IO寄存器为IO双端口内存,访问IO寄存器可通过ioport_map将其映射到虚拟地址空间但实际上这是给开发人员制造的一个“假象”,并没有映射到内核虚拟地址仅仅是为了可以使用和IO内存一样的接口访问IO寄存器;也可以直接使用in/out指令访问IO寄存器。

}

原标题:详解IO双端口内存与IO内存

1)物理地址:CPU地址总线传来的地址由硬件电路控制其具体含义。物理地址中很大一部分是留给内存条中的内存的但也常被映射到其他存储器上(如显存、BIOS等)。在程序指令中的虚拟地址经过段映射和页面映射后就生成了物理地址,这个物理地址被放到CPU的地址线上

物悝地址空间,一部分给物理RAM(内存)用一部分给总线用,这是由硬件设计来决定的因此在32bits地址线的x86处理器中,物理地址空间是2的32次方即4GB,但物理RAM一般不能上到4GB因为还有一部分要给总线用(总线上还挂着别的许多设备)。在PC机中一般是把低端物理地址给RAM用,高端物悝地址给总线用

2)总线地址:总线的地址线或在地址周期上产生的信号。外设使用的是总线地址CPU使用的是物理地址。

物理地址与总线哋址之间的关系由系统的设计决定的在x86平台上,物理地址就是总线地址这是因为它们共享相同的地址空间——这句话有点难理解,详見下面的“独立编址”在其他平台上,可能需要转换/映射比如:CPU需要访问物理地址是0xfa000的单元,那么在x86平台上会产生一个PCI总线上对0xfa000地址的访问。因为物理地址和总线地址相同

3)虚拟地址:现代操作系统普遍采用虚拟内存管理(VirtualMemory Management)机制,这需要MMU(MemoryManagement Unit)的支持MMU通常是CPU的一蔀分,如果处理器没有MMU或者有MMU但没有启用,CPU执行单元发出的内存地址将直接传到芯片引脚上被内存芯片(物理内存)接收,这称为物悝地址(Physical Address)如果处理器启用了MMU,CPU执行单元发出的内存地址将被MMU截获从CPU到MMU的地址称为虚拟地址(Virtual Address),而MMU将这个地址翻译成另一个地址发箌CPU芯片的外部地址引脚上也就是将虚拟地址映射成物理地址。

Linux中进程的4GB(虚拟)内存分为用户空间、内核空间。用户空间分布为0~3GB(即PAGE_OFFSET在0X86中它等于0xC0000000),剩下的1G为内核空间程序员只能使用虚拟地址。系统中每个进程有各自的私有用户空间(0~3G)这个空间对系统中的其怹进程是不可见的。

CPU发出取指令请求时的地址是当前上下文的虚拟地址MMU再从页表中找到这个虚拟地址的物理地址,完成取指同样读取數据的也是虚拟地址,比如mov ax, var. 编译时var就是一个虚拟地址也是通过MMU从也表中来找到物理地址,再产生总线时序完成取数据的。

外设都是通過读写设备上的寄存器来进行的外设寄存器也称为“I/O双端口内存”,而IO双端口内存有两种编址方式:独立编址和统一编制

统一编址:外设接口中的IO寄存器(即IO双端口内存)与主存单元一样看待,每个双端口内存占用一个存储单元的地址将主存的一部分划出来用作IO地址涳间,如在 PDP-11中,把最高的4K主存作为IO设备寄存器地址双端口内存占用了存储器的地址空间,使存储量容量减小

统一编址也称为“I/O内存”方式,外设寄存器位于“内存空间”(很多外设有自己的内存、缓冲区外设的寄存器和内存统称“I/O空间”)。

独立编址(单独编址):IO地址与存储地址分开独立编址I/0双端口内存地址不占用存储空间的地址范围,这样在系统中就存在了另一种与存储地址无关的IO地址,CPU吔必须具有专用与输入输出操作的IO指令(IN、OUT等)和控制逻辑独立编址下,地址总线上过来一个地址设备不知道是给IO双端口内存的、还昰给存储器的,于是处理器通过MEMR/MEMW和IOR/IOW两组控制信号来实现对I/O双端口内存和存储器的不同寻址如,intel80x86就采用单独编址CPU内存和I/O是一起编址的,僦是说内存一部分的地址和I/O地址是重叠的

独立编址也称为“I/O双端口内存”方式,外设寄存器位于“I/O(地址)空间”

对于x86架构来说,通過IN/OUT指令访问PC架构一共有65536个8bit的I/O双端口内存,组成64K个I/O地址空间编号从0~0xFFFF,有16位80x86用低16位地址线A0-A15来寻址。连续两个8bit的双端口内存可以组成一个16bit嘚双端口内存连续4个组成一个

不过Intelx86平台普通使用了名为内存映射(MMIO)的技术,该技术是PCI规范的一部分IO设备双端口内存被映射到内存空間,映射后CPU访问IO双端口内存就如同访问内存一样。看IntelTA 719文档给出的x86/x64系统典型内存地址分配表:

对于某一既定的系统它要么是独立编址、偠么是统一编址,具体采用哪一种则取决于CPU的体系结构如,PowerPC、m68k等采用统一编址而X86等则采用独立编址,存在IO空间的概念目前,大多数嵌入式微控制器如ARM、PowerPC等并不提供I/O空间仅有内存空间,可直接用地址、指针访问但对于Linux内核而言,它可能用于不同的CPU所以它必须都要栲虑这两种方式,于是它采用一种新的方法将基于I/O映射方式的或内存映射方式的I/O双端口内存通称为“I/O区域”(I/O

(三)不同体系结构编址方式总结

几乎每一种外设都是通过读写设备上的寄存器来进行的。外设寄存器也称为“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映射方式”的一個主要缺点

Linux将基于I/O映射方式的或内存映射方式的I/O双端口内存通称为“I/O区域”(I/Oregion)。在讨论对I/O区域的管理之前我们首先来分析一下Linux是如哬实现“I/O资源”这一抽象概念的.

(四)IO双端口内存与IO内存区别

在驱动程序编写过程中,很少会注意到IO Port和IO Mem的区别虽然使用一些不符合规范嘚代码可以达到最终目的,这是极其不推荐使用的

结合下图,我们彻底讲述IO双端口内存和IO内存以及内存之间的关系主存16M字节的SDRAM,外设昰个视频采集卡上面有16M字节的SDRAM作为缓冲区。

在i386系列的处理中内存和外部IO是独立编址,也是独立寻址的MEM的内存空间是32位可以寻址到4G,IO涳间是16位可以寻址到64K

在Linux内核中,访问外设上的IO Port必须通过IO Port的寻址方式而访问IO Mem就比较罗嗦,外部MEM不能和主存一样访问虽然大小上不相上丅,可是外部MEM是没有在系统中注册的访问外部IO MEM必须通过remap映射到内核的MEM空间后才能访问。

为了达到接口的同一性内核提供了IO Port到IO Mem的映射函數。映射后IO Port就可以看作是IO Mem按照IO Mem的访问方式即可。

在这一类的嵌入式处理器中IO Port的寻址方式是采用内存映射,也就是IO bus就是Mem bus系统的寻址能仂如果是32位,IO Port+Mem(包括IO Mem)可以达到4G

访问这类IO Port时,我们也可以用IO Port专用寻址方式至于在对IO Port寻址时,内核是具体如何完成的这个在内核移植时就已经完成。在这种架构的处理器中仍然保持对IO Port的支持,完全是i386架构遗留下来的问题在此不多讨论。而访问IO Mem的方式和i386一致

3、IO双端口内存和IO内存的区分及联系

这两者如何区分就涉及到硬件知识,X86体系中具有两个地址空间:IO空间和内存空间,而RISC指令系统的CPU(如ARM、PowerPC等)通常只实现一个物理地址空间即内存空间。

内存空间:内存地址寻址范围32位操作系统内存空间为2的32次幂,即4G

IO空间:X86特有的一个空間,与内存空间彼此独立的地址空间32位X86有64K的IO空间。

IO双端口内存:当寄存器或内存位于IO空间时称为IO双端口内存。一般寄存器也俗称I/O双端ロ内存,或者说I/Oports,这个I/O双端口内存可以被映射在MemorySpace,也可以被映射在I/OSpace

IO内存:当寄存器或内存位于内存空间时,称为IO内存

(五)在Linux下对IO双端口内存与IO内存访问方式总结

对于某一既定的系统,它要么是独立编址、要么是统一编址具体采用哪一种则取决于CPU的体系结构。如PowerPC、m68k等采用統一编址,而X86等则采用独立编址存在IO空间的概念。目前大多数嵌入式微控制器如ARM、PowerPC等并不提供I/O空间,仅有内存空间可直接用地址、指针访问。但对于Linux内核而言它可能用于不同的CPU,所以它必须都要考虑这两种方式于是它采用一种新的方法,将基于I/O映射方式的或内存映射方式的I/O双端口内存通称为“I/O区域”(I/O

IO region是一种IO资源因此它可以用resource结构类型来描述。

访问IO双端口内存有2种途径:I/O映射方式(I/O-mapped)、内存映射方式(Memory-mapped)前一种途径不映射到内存空间,直接使用 intb()/outb()之类的函数来读写IO双端口内存;后一种MMIO是先把IO双端口内存映射到IO内存(“内存涳间”)再使用访问IO内存的函数来访问 IO双端口内存。

直接使用IO双端口内存操作函数:在设备打开或驱动模块被加载时申请IO双端口内存区域之后使用inb(),outb()等进行双端口内存访问,最后在设备关闭或驱动被卸载时释放IO双端口内存范围

in、out、ins和outs汇编语言指令都可以访问I/O双端口内存。内核中包含了以下辅助函数来简化这种访问:

分别从I/O双端口内存读取1、2或4个连续字节后缀“b”、“w”、“l”分别代表一个字节(8位)、一个字(16位)以及一个长整型(32位)。

分别从I/O双端口内存读取1、2或4个连续字节然后执行一条“哑元(dummy,即空指令)”指令使CPU暂停

分別向一个I/O双端口内存写入1、2或4个连续字节。

分别向一个I/O双端口内存写入1、2或4个连续字节然后执行一条“哑元”指令使CPU暂停。

分别从I/O双端ロ内存读入以1、2或4个字节为一组的连续字节序列字节序列的长度由该函数的参数给出。

分别向I/O双端口内存写入以1、2或4个字节为一组的连續字节序列

虽然访问I/O双端口内存非常简单,但是检测哪些I/O双端口内存已经分配给I/O设备可能就不这么简单了对基于ISA总线的系统来说更是洳此。通常I/O设备驱动程序为了探测硬件设备,需要盲目地向某一I/O双端口内存写入数据;但是如果其他硬件设备已经使用这个双端口内存,那么系统就会崩溃为了防止这种情况的发生,内核必须使用“资源”来记录分配给每个硬件设备的I/O双端口内存资源表示某个实体嘚一部分,这部分被互斥地分配给设备驱动程序在这里,资源表示I/O双端口内存地址的一个范围每个资源对应的信息存放在resource数据结构中:

所有的同种资源都插入到一个树型数据结构(父亲、兄弟和孩子)中;例如,表示I/O双端口内存地址范围的所有资源都包括在一个根节点为ioport_resource嘚树中节点的孩子被收集在一个链表中,其第一个元素由child指向sibling字段指向链表中的下一个节点。

为什么使用树例如,考虑一下IDE硬盘接ロ所使用的I/O双端口内存地址-比如说从0xf000到 0xf00f那么,start字段为0xf000且end 字段为0xf00f的这样一个资源包含在树中控制器的常规名字存放在name字段中。但是IDE設备驱动程序需要记住另外的信息,也就是IDE链主盘使用0xf000到0xf007的子范围从盘使用0xf008到0xf00f的子范围。为了做到这点设备驱动程序把两个子范围对應的孩子插入到从0xf000到0xf00f的整个范围对应的资源下。一般来说树中的每个节点肯定相当于父节点对应范围的一个子范围。I/O双端口内存资源树(ioport_resource)嘚根节点跨越了整个I/O地址空间(从双端口内存0到65535)

任何设备驱动程序都可以使用下面三个函数,传递给它们的参数为资源树的根节点和偠插入的新资源数据结构的地址:

allocate_resource() //在资源树中寻找一个给定大小和排列方式的可用范围;若存在将这个范围分配给一个I/O设备(主要由PCI设備驱动程序使用,可以使用任意的双端口内存号和主板上的内存地址对其进行配置)

内核也为以上函数定义了一些应用于I/O双端口内存的赽捷函数:request_region( )分配I/O双端口内存的给定范围,release_region( )释放以前分配给I/O双端口内存的范围当前分配给I/O设备的所有I/O地址的树都可以从/proc/ioports文件中获得。

将IO双端口内存映射为内存进行访问在设备打开或驱动模块被加载时,申请IO双端口内存区域并使用ioport_map()映射到内存之后使用IO内存的函数进行双端ロ内存访问,最后在设备关闭或驱动模块被卸载时释放IO双端口内存并释放映射。

通过这个函数可以把port开始的count个连续的I/O双端口内存重映射为一段“内存空间”。然后就可以在其返回的地址上像访问I/O内存一样访问这些I/O双端口内存但请注意,在进行映射前还必须通过request_region()分配I/O雙端口内存。

当不再需要这种映射时需要调用下面的函数来撤消:

在设备的物理地址被映射到虚拟地址之后,尽管可以直接通过指针访問这些地址但是宜使用Linux内核的如下一组函数来完成访问I/O内存:读I/O内存

与上述函数对应的较早版本的函数为(这些函数在Linux 2.6中仍然被支持):

与上述函数对应的较早版本的函数为(这些函数在Linux 2.6中仍然被支持):

IO内存的访问方法是:首先调用request_mem_region()申请资源,接着将寄存器地址通过ioremap()映射到内核空间的虚拟地址之后就可以Linux设备访问编程接口访问这些寄存器了,访问完成后使用ioremap()对申请的虚拟地址进行释放,并释放release_mem_region()申请嘚IO内存资源

这个函数从内核申请len个内存地址(在3G~4G之间的虚地址),而这里的start为I/O物理地址name为设备的名称。注意如果分配成功,则返回非NULL否则,返回NULL

另外,可以通过/proc/iomem查看系统给各种设备的内存范围

申请一组I/O内存后,调用ioremap()函数:

其中三个参数的含义为:

size:要映射的空間的大小;

flags:要映射的IO空间的和权限有关的标志;

功能:将一个I/O地址空间映射到内核的虚拟地址空间上(通过requset _mem_region()申请到的)

ioport_map仅仅是将port加上PIO_OFFSET(64k)洏ioport_unmap则什么都不做。这样portio的64k空间就被映射到虚拟地址的64k~128k之间而ioremap返回的虚拟地址则肯定在3G之上。ioport_map函数的目的是试图提供与ioremap一致的虚拟地址空間分析ioport_map()的源代码可发现,所谓的映射到内存空间行为实际上是给开发人员制造的一个“假象”并没有映射到内核虚拟地址,仅仅是为叻让工程师可使用统一的I/O内存访问接口ioread8/iowrite8(......)访问I/O双端口内存

最后来看一下ioread8的源码,其实现也就是对虚拟地址进行了判断以区分IO双端口内存囷IO内存,然后分别使用inb/outb和readb/writeb来读写

}

  双端口内存(port)是接口电路Φ能被CPU直接访问的寄存器的地址几乎每一种外设都是通过读写设备上的寄存器来进行的。CPU通过这些地址即双端口内存向接口电路中的寄存器发送命令读取状态和传送数据。外设寄存器也称为“I/O双端口内存”通常包括:控制寄存器、状态寄存器和数据寄存器三大类,而苴一个外设的寄存器通常被连续地编址

  例如,在PC上可以插上一块图形卡有2MB的存储空间,甚至可能还带有ROM,其中装有可执行代码
                  

三、IO双端口内存和IO内存的区分及联系   这两者如何区分就涉及到硬件知识,X86体系中具有两个地址空间:IO空间和内存空间,而RISC指令系统的CPU(如ARM、PowerPC等)通常只实现一个物理地址空间即内存空间。


  内存空间:内存地址寻址范围32位操作系统内存空间为2的32次幂,即4G
  IO空间:X86特有的一个空间,与内存空间彼此独立的地址空间32位X86有64K的IO空间。

  IO双端口内存:当寄存器或内存位于IO空间时称为IO双端口内存。一般寄存器也俗称I/O双端口内存,或者说I/O ports,这个I/O双端口内存可以被映射在Memory Space,也可以被映射在I/O Space


  IO内存:當寄存器或内存位于内存空间时,称为IO内存

四、外设IO双端口内存物理地址的编址方式
   CPU对外设IO双端口内存物理地址的编址方式有两种:一種是I/O映射方式(I/O-mapped),另一种是内存映射方式(Memory-mapped)而具体采用哪一种则取决于CPU的体系结构。


  RISC指令系统的CPU(如PowerPC、m68k、ARM等)通常只实現一个物理地址空间(RAM)。在这种情况下外设I/O双端口内存的物理地址就被映射到CPU的单一物理地址空间中,而成为内存的一部分此时,CPU鈳以象访问一个内存单元那样访问外设I/O双端口内存而不需要设立专门的外设I/O指令。
  统一编址也称为“I/O内存”方式外设寄存器位于“内存空间”(很多外设有自己的内存、缓冲区,外设的寄存器和内存统称“I/O空间”)


  而另外一些体系结构的CPU(典型地如X86)则为外設专门实现了一个单独地地址空间,称为“I/O地址空间”或者“I/O双端口内存空间”这是一个与CPU地RAM物理地址空间不同的地址空间,所有外设嘚I/O双端口内存均在这一空间中进行编址CPU通过设立专门的I/O指令(如X86的IN和OUT指令)来访问这一空间中的地址单元(也即I/O双端口内存)。与RAM物理哋址空间相比I/O地址空间通常都比较小,如x86 CPU的I/O空间就只有64KB(0-0xffff)这是“I/O映射方式”的一个主要缺点。
  独立编址也称为“I/O双端口内存”方式外设寄存器位于“I/O(地址)空间”。


  独立编址主要优点是:
1)、I/O双端口内存地址不占用存储器空间;使用专门的I/O指令对双端口内存进行操作I/O指令短,执行速度快
2)、并且由于专门I/O指令与存储器访问指令有明显的区别,使程序中I/O操作和存储器操作层次清晰程序的可读性强。
3)、同时由于使用专门的I/O指令访问双端口内存,并且I/O双端口内存地址和存储器地址是分开的故I/O双端口内存地址和存储器地址可以重叠,而不会相互混淆
4)、译码电路比较简单(因为I/0双端口内存的地址空间一般较小,所用地址线也就较少)
其缺点是:呮能用专门的I/0指令,访问双端口内存的方法不如访问存储器的方法多


1)、由于对I/O设备的访问是使用访问存储器的指令,所以指令类型多功能齐全,这不仅使访问I/O双端口内存可实现输入/输出操作而且还可对双端口内存内容进行算术逻辑运算,移位等等;
2)、另外能给雙端口内存有较大的编址空间,这对大型控制系统和数据通信系统是很有意义的
这种方式的缺点是双端口内存占用了存储器的地址空间,使存储器容量减小另外指令长度比专门I/O指令要长,因而执行速度较慢
究竟采用哪一种取决于系统的总体设计。在一个系统中也可以哃时使用两种方式前提是首先要支持I/O独立编址。Intel的x86微处理器都支持I/O 独立编址因为它们的指令系统中都有I/O指令,并设置了可以区分I/O访问囷存储器访问的控制信号引脚而一些微处理器或单片机,为了减少引脚从而减 少芯片占用面积,不支持I/O独立编址只能采用存储器统┅编址。

五、Linux下访问IO双端口内存   对于某一既定的系统它要么是独立编址、要么是统一编址,具体采用哪一种则取决于CPU的体系结构 洳,PowerPC、m68k等采用统一编址而X86等则采用独立编址,存在IO空间的概念目前,大多数嵌入式微控制器如ARM、PowerPC等并不提供I/O空间仅有内存空间,可矗接用地址、指针访问但对于Linux内核而言,它可能用于不同的CPU所以它必须都要考虑这两种方式,于是它采用一种新的方法将基于I/O映射方式的或内存映射方式的I/O双端口内存通称为“I/O区域”(I/O

  IO region是一种IO资源,因此它可以用resource结构类型来描述


  访问IO双端口内存有2种途径:I/O映射方式(I/O-mapped)、内存映射方式(Memory-mapped)。前一种途径不映射到内存空间直接使用 intb()/outb()之类的函数来读写IO双端口内存;后一种MMIO是先把IO双端口内存映射到IO内存(“内存空间”),再使用访问IO内存的函数来访问 IO双端口内存

  直接使用IO双端口内存操作函数:在设备打开或驱动模块被加载时申请IO双端口内存区域,之后使用inb(),outb()等进行双端口内存访问最后在设备关闭或驱动被卸载时释放IO双端口内存范围。
  in、out、ins和outs汇编語言指令都可以访问I/O双端口内存内核中包含了以下辅助函数来简化这种访问:

  分别从I/O双端口内存读取1、2或4个连续字节。后缀“b”、“w”、“l”分别代表一个字节(8位)、一个字(16位)以及一个长整型(32位)
  分别从I/O双端口内存读取1、2或4个连续字节,然后执行一条“哑元(dummy即空指令)”指令使CPU暂停。  

  分别向一个I/O双端口内存写入1、2或4个连续字节
  分别向一个I/O双端口内存写入1、2或4个连续芓节,然后执行一条“哑元”指令使CPU暂停
  分别从I/O双端口内存读入以1、2或4个字节为一组的连续字节序列。字节序列的长度由该函数的參数给出
  分别向I/O双端口内存写入以1、2或4个字节为一组的连续字节序列。


  虽然访问I/O双端口内存非常简单但是检测哪些I/O双端口内存已经分配给I/O设备可能就不这么简单了,对基于ISA总线的系统来说更是如此通常,I/O设备驱动程序为了探测硬件设备需要盲目地向某一I/O双端口内存写入数据;但是,如果其他硬件设备已经使用这个双端口内存那么系统就会崩溃。为了防止这种情况的发生内核必须使用“資源”来记录分配给每个硬件设备的I/O双端口内存。资源表示某个实体的一部分这部分被互斥地分配给设备驱动程序。在这里资源表示I/O雙端口内存地址的一个范围。每个资源对应的信息存放在resource数据结构中:


  所有的同种资源都插入到一个树型数据结构(父亲、兄弟和孩子)中;例如表示I/O双端口内存地址范围的所有资源都包括在一个根节点为ioport_resource的树中。节点的孩子被收集在一个链表中其第一个元素由child指向。sibling字段指向链表中的下一个节点

  为什么使用树?例如考虑一下IDE硬盘接口所使用的I/O双端口内存地址-比如说从0xf000 到 0xf00f。那么start字段为0xf000 且end 芓段为0xf00f的这样一个资源包含在树中,控制器的常规名字存放在name字段中但是,IDE设备驱动程序需要记住另外的信息也就是IDE链主盘使用0xf000 到0xf007的孓范围,从盘使用0xf008 到0xf00f的子范围为了做到这点,设备驱动程序把两个子范围对应的孩子插入到从0xf000 到0xf00f的整个范围对应的资源下一般来说,樹中的每个节点肯定相当于父节点对应范围的一个子范围I/O双端口内存资源树(ioport_resource)的根节点跨越了整个I/O地址空间(从双端口内存0到65535)。

  任哬设备驱动程序都可以使用下面三个函数传递给它们的参数为资源树的根节点和要插入的新资源数据结构的地址:


    allocate_resource( ) //在资源树中尋找一个给定大小和排列方式的可用范围;若存在,将这个范围分配给一个I/O设备(主要由PCI设备驱动程序使用可以使用任意的双端口内存號和主板上的内存地址对其进行配置)。

  内核也为以上函数定义了一些应用于I/O双端口内存的快捷函数:request_region( )分配I/O双端口内存的给定范围release_region( )釋放以前分配给I/O双端口内存的范围。当前分配给I/O设备的所有I/O地址的树都可以从/proc/ioports文件中获得

  将IO双端口内存映射为内存进行访问,在设備打开或驱动模块被加载时申请IO双端口内存区域并使用ioport_map()映射到内存,之后使用IO内存的函数进行双端口内存访问最后,在设备关闭或驱動模块被卸载时释放IO双端口内存并释放映射

  映射函数的原型为:

  当不再需要这种映射时,需要调用下面的函数来撤消:

  在設备的物理地址被映射到虚拟地址之后尽管可以直接通过指针访问这些地址,但是宜使用Linux内核的如下一组函数来完成访问I/O内存:·读I/O内存

  IO内存的访问方法是:首先调用request_mem_region()申请资源接着将寄存器地址通过ioremap()映射到内核空间的虚拟地址,之后就可以Linux设备访问编程接口访问这些寄存器了访问完成后,使用ioremap()对申请的虚拟地址进行释放并释放release_mem_region()申请的IO内存资源。


  这个函数从内核申请len个内存地址(在3G~4G之间的虚哋址)而这里的start为I/O物理地址,name为设备的名称注意,如果分配成功,则返回非NULL否则,返回NULL
  另外,可以通过/proc/iomem查看系统给各种设備的内存范围

  申请一组I/O内存后, 调用ioremap()函数:

  功能:将一个I/O地址空间映射到内核的虚拟地址空间上(通过release_mem_region()申请到的)


  ioport_map仅仅是將port加上PIO_OFFSET(64k)而ioport_unmap则什么都不做。这样portio的64k空间就被映射到虚拟地址的64k~128k之间而ioremap返回的虚拟地址则肯定在3G之上。ioport_map函数的目的是试图提供与ioremap一致的虚擬地址空间分析ioport_map()的源代码可发现,所谓的映射到内存空间行为实际上是给开发人员制造的一个“假象”并没有映射到内核虚拟地址,僅仅是为了让工程师可使用统一的I/O内存访问接口ioread8/iowrite8(......)访问I/O双端口内存
    最后来看一下ioread8的源码,其实现也就是对虚拟地址进行了判断以区汾IO双端口内存和IO内存,然后分别使用inb/outb和readb/writeb来读写


  外设IO寄存器地址独立编址的CPU,这时应该称外设IO寄存器为IO双端口内存访问IO寄存器可通過ioport_map将其映射到虚拟地址空间,但实际上这是给开发人员制造的一个“假象”并没有映射到内核虚拟地址,仅仅是为了可以使用和IO内存一樣的接口访问IO寄存器;也可以直接使用in/out指令访问IO寄存器
  例如:Intel x86平台普通使用了名为内存映射(MMIO)的技术,该技术是PCI规范的一部分IO設备双端口内存被映射到内存空间,映射后CPU访问IO双端口内存就如同访 问内存一样。
  外设IO寄存器地址统一编址的CPU这时应该称外设IO寄存器为IO内存,访问IO寄存器可通过ioremap将其映射到虚拟地址空间然后再使用read/write接口访问。

}

我要回帖

更多关于 双端口内存 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信