收藏 分享(赏)

linux系统对io端口和io内存的管理.doc

上传人:cjc2202537 文档编号:1317478 上传时间:2018-06-23 格式:DOC 页数:8 大小:30KB
下载 相关 举报
linux系统对io端口和io内存的管理.doc_第1页
第1页 / 共8页
linux系统对io端口和io内存的管理.doc_第2页
第2页 / 共8页
linux系统对io端口和io内存的管理.doc_第3页
第3页 / 共8页
linux系统对io端口和io内存的管理.doc_第4页
第4页 / 共8页
linux系统对io端口和io内存的管理.doc_第5页
第5页 / 共8页
点击查看更多>>
资源描述

1、Linux 系统对 IO 端口和 IO 内存的管理日期:2012-01-17 来源:Linux 社区 作者:ce123 一、I/O 端口端口(port)是接口电路中能被 CPU 直接访问的寄存器的地址。几乎每一种外设都是通过读写设备上的寄存器来进行的。CPU 通过这些地址即端口向接口电路中的寄存器发送命令,读取状态和传送数据。外设寄存器也称为“I/O 端口” ,通常包括:控制寄存器、状态寄存器和数据寄存器三大类,而且一个外设的寄存器通常被连续地编址。 二、IO 内存例如,在 PC 上可以插上一块图形卡,有 2MB 的存储空间,甚至可能还带有 ROM,其中装有可执行代码。三、IO 端口和 IO

2、内存的区分及联系这两者如何区分就涉及到硬件知识,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,也可以被映

3、射在 I/O Space。IO 内存:当寄存器或内存位于内存空间时,称为 IO 内存。四、外设 IO 端口物理地址的编址方式CPU 对外设 IO 端口物理地址的编址方式有两种:一种是 I/O 映射方式(I/Omapped) ,另一种是内存映射方式(Memorymapped) 。而具体采用哪一种则取决于 CPU 的体系结构。1、统一编址RISC 指令系统的 CPU(如,PowerPC、m68k、ARM 等)通常只实现一个物理地址空间(RAM) 。在这种情况下,外设 I/O 端口的物理地址就被映射到 CPU 的单一物理地址空间中,而成为内存的一部分。此时,CPU 可以象访问一个内存单元那样访问外设

4、 I/O 端口,而不需要设立专门的外设 I/O 指令。统一编址也称为“I/O 内存”方式,外设寄存器位于“内存空间” (很多外设有自己的内存、缓冲区,外设的寄存器和内存统称“I/O 空间” ) 。2、独立编址 而另外一些体系结构的 CPU(典型地如 X86)则为外设专门实现了一个单独地地址空间,称为“I/O 地址空间”或者“I/O 端口空间” 。这是一个与 CPU 地 RAM 物理地址空间不同的地址空间,所有外设的 I/O 端口均在这一空间中进行编址。CPU 通过设立专门的I/O 指令(如 X86 的 IN 和 OUT 指令)来访问这一空间中的地址单元(也即 I/O 端口) 。与RAM 物理地

5、址空间相比,I/O 地址空间通常都比较小,如 x86 CPU 的 I/O 空间就只有64KB( 00xffff) 。这是“I/O 映射方式”的一个主要缺点。独立编址也称为“I/O 端口”方式,外设寄存器位于“I/O (地址)空间” 。3、优缺点独立编址主要优点是:1) 、 I/O 端口地址不占用存储器空间;使用专门的 IO 指令对端口进行操作,I/O 指令短,执行速度快。2) 、并且由于专门 I/O 指令与存储器访问指令有明显的区别,使程序中 I/O 操作和存储器操作层次清晰,程序的可读性强。3) 、同时,由于使用专门的 I/O 指令访问端口,并且 I/O 端口地址和存储器地址是分开的,故 I

6、/O 端口地址和存储器地址可以重叠,而不会相互混淆。 4) 、译码电路比较简单(因为 I/0 端口的地址空间一般较小,所用地址线也就较少) 。其缺点是:只能用专门的 I/0 指令,访问端口的方法不如访问存储器的方法多。统一编址优点:1) 、由于对 I/O 设备的访问是使用访问存储器的指令,所以指令类型多,功能齐全,这不仅使访问 I/O 端口可实现输入/输出操作,而且还可对端口内容进行算术逻辑运算,移位等等;2) 、另外,能给端口有较大的编址空间,这对大型控制系统和数据通信系统是很有意义的。这种方式的缺点是端口占用了存储器的地址空间,使存储器容量减小,另外指令长度比专门 I/O 指令要长,因而执

7、行速度较慢。究竟采用哪一种取决于系统的总体设计。在一个系统中也可以同时使用两种方式,前提是首先要支持 I/O 独立编址。Intel 的 x86 微处理器都支持 I/O 独立编址,因为它们的指令系统中都有 I/O 指令,并设置了可以区分 I/O 访问和存储器访问的控制信号引脚。而一些微处理器或单片机,为了减少引脚,从而减 少芯片占用面积,不支持 I/O 独立编址,只能采用存储器统一编址。五、Linux 下访问 IO 端口对于某一既定的系统,它要么是独立编址、要么是统一编址,具体采用哪一种则取决于 CPU 的体系结构。 如,PowerPC、m68k 等采用统一编址,而 X86 等则采用独立编址,存

8、在 IO 空间的概念。目前,大多数嵌入式微控制器如 ARM、PowerPC 等并不提供I/O 空间,仅有内存空间,可直接用地址、指针访问。但对于 Linux 内核而言,它可能用于不同的 CPU,所以它必须都要考虑这两种方式,于是它采用一种新的方法,将基于 I/O 映射方式的或内存映射方式的 I/O 端口通称为“I/O 区域” (I/O region) ,不论你采用哪种方式,都要先申请 IO 区域:request_resource(),结束时释放它:release_resource() 。IO region 是一种 IO 资源,因此它可以用 resource 结构类型来描述。访问 IO 端口有

9、2 种途径:I/O 映射方式(I/Omapped ) 、内存映射方式(Memorymapped) 。前一种途径不映射到内存空间,直接使用 intb()/outb()之类的函数来读写 IO 端口;后一种 MMIO 是先把 IO 端口映射到 IO 内存(“内存空间” ) ,再使用访问IO 内存的函数来访问 IO 端口。1、 I/O 映射方式直接使用 IO 端口操作函数:在设备打开或驱动模块被加载时申请 IO 端口区域,之后使用 inb(),outb()等进行端口访问,最后在设备关闭或驱动被卸载时释放 IO 端口范围。in、 out、ins 和 outs 汇编语言指令都可以访问 I/O 端口。内核中

10、包含了以下辅助函数来简化这种访问:inb( )、 inw( )、inl( )分别从 I/O 端口读取 1、2 或 4 个连续字节。后缀“b” 、 “w”、 “l”分别代表一个字节(8 位)、一个字(16 位)以及一个长整型(32 位) 。inb_p( )、inw_p( )、inl_p( )分别从 I/O 端口读取 1、2 或 4 个连续字节,然后执行一条 “哑元(dummy,即空指令) ”指令使 CPU 暂停。outb( )、 outw( )、outl( )分别向一个 I/O 端口写入 1、2 或 4 个连续字节。outb_p( )、outw_p( )、outl_p( )分别向一个 I/O 端

11、口写入 1、2 或 4 个连续字节,然后执行一条 “哑元”指令使 CPU 暂停。insb( )、insw( )、insl( )分别从 I/O 端口读入以 1、2 或 4 个字节为一组的连续字节序列。字节序列的长度由该函数的参数给出。outsb( )、outsw( )、outsl( )分别向 I/O 端口写入以 1、2 或 4 个字节为一组的连续字节序列。流程如下:虽然访问 I/O 端口非常简单,但是检测哪些 I/O 端口已经分配给 I/O 设备可能就不这么简单了,对基于 ISA 总线的系统来说更是如此。通常, I/O 设备驱动程序为了探测硬件设备,需要盲目地向某一 I/O 端口写入数据;但是,

12、如果其他硬件设备已经使用这个端口,那么系统就会崩溃。为了防止这种情况的发生,内核必须使用“资源”来记录分配给每个硬件设备的 I/O 端口。资源表示某个实体的一部分,这部分被互斥地分配给设备驱动程序。在这里,资源表示 I/O 端口地址的一个范围。每个资源对应的信息存放在 resource 数据结构中:plain struct resource resource_size_t start;/ 资源范围的开始 resource_size_t end;/ 资源范围的结束 const char *name; /资源拥有者的名字 unsigned long flags;/ 各种标志 struct reso

13、urce *parent, *sibling, *child;/ 指向资源树中父亲,兄弟和孩子的指针 ; 所有的同种资源都插入到一个树型数据结构(父亲、兄弟和孩子)中;例如,表示 I/O 端口地址范围的所有资源都包括在一个根节点为 ioport_resource 的树中。节点的孩子被收集在一个链表中,其第一个元素由 child 指向。sibling 字段指向链表中的下一个节点。为什么使用树?例如,考虑一下 IDE 硬盘接口所使用的 I/O 端口地址比如说从0xf000 到 0xf00f。那么,start 字段为 0xf000 且 end 字段为 0xf00f 的这样一个资源包含在树中,控制器的

14、常规名字存放在 name 字段中。但是,IDE 设备驱动程序需要记住另外的信息,也就是 IDE 链主盘使用 0xf000 到 0xf007 的子范围,从盘使用 0xf008 到 0xf00f 的子范围。为了做到这点,设备驱动程序把两个子范围对应的孩子插入到从 0xf000 到 0xf00f 的整个范围对应的资源下。一般来说,树中的每个节点肯定相当于父节点对应范围的一个子范围。I/O 端口资源树(ioport_resource) 的根节点跨越了整个 I/O 地址空间(从端口 0 到65535) 。任何设备驱动程序都可以使用下面三个函数,传递给它们的参数为资源树的根节点和要插入的新资源数据结构的地

15、址:request_resource( ) /把一个给定范围分配给一个 I/O 设备。allocate_resource( ) /在资源树中寻找一个给定大小和排列方式的可用范围;若存在,将这个范围分配给一个 I/O 设备(主要由 PCI 设备驱动程序使用,可以使用任意的端口号和主板上的内存地址对其进行配置) 。release_resource( ) /释放以前分配给 I/O 设备的给定范围。内核也为以上函数定义了一些应用于 I/O 端口的快捷函数:request_region( )分配 I/O 端口的给定范围,release_region( )释放以前分配给 I/O 端口的范围。当前分配给 I

16、/O 设备的所有I/O 地址的树都可以从/proc/ioports 文件中获得。2、内存映射方式将 IO 端口映射为内存进行访问,在设备打开或驱动模块被加载时,申请 IO 端口区域并使用 ioport_map()映射到内存,之后使用 IO 内存的函数进行端口访问,最后,在设备关闭或驱动模块被卸载时释放 IO 端口并释放映射。映射函数的原型为:void *ioport_map(unsigned long port, unsigned int count);通过这个函数,可以把 port 开始的 count 个连续的 I/O 端口重映射为一段“内存空间” 。然后就可以在其返回的地址上像访问 I/O

17、 内存一样访问这些 I/O 端口。但请注意,在进行映射前,还必须通过 request_region( )分配 I/O 端口。当不再需要这种映射时,需要调用下面的函数来撤消:void ioport_unmap(void *addr);在设备的物理地址被映射到虚拟地址之后,尽管可以直接通过指针访问这些地址,但是宜使用 Linux 内核的如下一组函数来完成访问 I/O 内存: 读 I/O 内存unsigned int ioread8(void *addr);unsigned int ioread16(void *addr);unsigned int ioread32(void *addr);与上述函

18、数对应的较早版本的函数为(这些函数在 Linux 2.6 中仍然被支持):unsigned readb(address);unsigned readw(address);unsigned readl(address);写 I/O 内存void iowrite8(u8 value, void *addr);void iowrite16(u16 value, void *addr);void iowrite32(u32 value, void *addr);与上述函数对应的较早版本的函数为(这些函数在 Linux 2.6 中仍然被支持):void writeb(unsigned value, ad

19、dress);void writew(unsigned value, address);void writel(unsigned value, address);流程如下:六、Linux 下访问 IO 内存IO 内存的访问方法是:首先调用 request_mem_region()申请资源,接着将寄存器地址通过 ioremap()映射到内核空间的虚拟地址,之后就可以 Linux 设备访问编程接口访问这些寄存器了,访问完成后,使用 ioremap()对申请的虚拟地址进行释放,并释放release_mem_region()申请的 IO 内存资源。struct resource *requset_me

20、m_region(unsigned long start, unsigned long len,char *name);这个函数从内核申请 len 个内存地址(在 3G4G 之间的虚地址) ,而这里的 start 为 I/O物理地址,name 为设备的名称。注意, 。如果分配成功,则返回非 NULL,否则,返回NULL。另外,可以通过/proc/iomem 查看系统给各种设备的内存范围。要释放所申请的 I/O 内存,应当使用 release_mem_region()函数:void release_mem_region(unsigned long start, unsigned long len

21、)申请一组 I/O 内存后, 调用 ioremap()函数:void * ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);其中三个参数的含义为:phys_addr:与 requset_mem_region 函数中参数 start 相同的 I/O 物理地址;size:要映射的空间的大小;flags:要映射的 IO 空间的和权限有关的标志;功能:将一个 I/O 地址空间映射到内核的虚拟地址空间上(通过 release_mem_region()申请到的)流程如下:六、ioremap 和 ioport_

22、map 下面具体看一下 ioport_map 和 ioport_umap 的源码:plain void _iomem *ioport_map(unsigned long port, unsigned int nr) if (port PIO_MASK) return NULL; return (void _iomem *) (unsigned long) (port + PIO_OFFSET); void ioport_unmap(void _iomem *addr) /* Nothing to do */ ioport_map 仅仅是将 port 加上 PIO_OFFSET(64k),而 i

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

24、内存,然后分别使用 inb/outb 和 readb/writeb 来读写。plain unsigned int fastcall ioread8(void _iomem *addr) IO_COND(addr, return inb(port), return readb(addr); #define VERIFY_PIO(port) BUG_ON(port if (port PIO_RESERVED) VERIFY_PIO(port); port is_pio; else is_mmio; while (0) 展开: unsigned int fastcall ioread8(void _

25、iomem *addr) unsigned long port = (unsigned long _force)addr; if( port 0x40000UL ) BUG_ON( (port port return inb(port); else return readb(addr); 七、总结外设 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 接口访问。本篇文章来源于 Linux 公社网站() 原文链接:http:/

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 经营企划

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报