IO端口,IO内存 ,IO映射 内存映射

Linux系统下IO Memory和IO Port的区别

2014年07月06日 Linux知识⁄ 共 721字 ⁄ 字号 小 中 大 评论 7 条

下面以x86架构为例,其它的架构可能并不适用。

先说一下本人对IO Memory和IO Port的理解。

IO Port叫做IO端口,主要用来指外设(如网卡、HBA卡)等的寄存器空间,而IO Memory叫做IO内存,有些外设可能不光有寄存器,还有其他的内存空间,这部分内存空间叫做IO内存,IO端口就相当于主板上CPU里面的寄存器,IO内存就相当于主板上的内存条。可以参考一下下面这张图:

IO

IO内存作为系统内存的一部分,外设的IO内存资源的物理地址是已知的,由硬件的设计决定,可以通过cat /proc/iomem命令查看外设的IO内存物理地址分布情况,访问IO内存前,必须先通过ioremap方式建立内存映射表(页表)后CPU才能够访问这些IO空间(访问系统内存前不也是需要建立映射表么,只不过不是通过ioremap方式建立的),因为CPU是不可以直接访问物理地址的,所以在访问前必须先为这些物理地址空间建立映射后CPU通过访问映射后的虚拟地址去访问这些IO内存。

IO端口即外设的寄存器空间,CPU访问外设的寄存器有两种方式,一种是直接将这些寄存器当成普通的IO内存来看待,这样CPU访问这些寄存器就跟访问IO内存方式是一样的了;另外一种方式是通过类似将这些寄存器进行编号(即IO端口号),然后通过特殊的端口访问方式来访问这些寄存器。系统的IO端口分布情况可以通过cat /proc/ioport方式查看,这里面列出了系统所有的IO端口分布情况,注意这边看到的地址不是物理地址,而是IO端口号的分布情况,跟物理地址没有关系,CPU访问外设寄存器就是通过传入这些端口号来访问外设寄存器的,而cat /proc/iomem这个里面看到的地址是物理地址的分布情况。







操作IO端口

对I/O端口的操作需按如下步骤完成:

1、申请

2、访问

3、释放

申请I/O端口:内核提供了一套函数来允许驱动申请它需要的I/O端口,其中核心的函数是:

struct resource *request_region(unsigned long first, unsigned long n, const char *name)

这个函数告诉内核,你要使用从first开始的n个端口,name参数是设备的名字。如果申请成功,返回非NULL,申请失败,返回NULL。

系统中端口的分配情况记录在/proc/ioports中(展示)。如果不能分配需要的端口,可以来这里查看谁在使用。

访问I/O端口:I/O端口可分为8-位,16-位,和32-位端口。linux内核头文件(体系依赖的头文件<asm/io.h>)定义了下列内联函数来访问I/O端口:

unsigned inb(unsigned port)

读字节端口(8位宽)

void outb(unsigned char byte, unsigned port)

写字节端口(8位宽)

unsigned inw(unsigned port)

void outw(unsigned short word, unsigned port)

存取16-位端口

unsigned inl(unsigned port)

void outl(unsigned longword, unsigned port)

存取32-位端口

释放I/O端口:

当用完一组I/O端口(通常在驱动卸载时),应使用如下函数把它们返还给系统:

void release_region(unsigned long start, unsigned long n)

操作I/O内存

对I/O内存的操作需按如下步骤完成:

1、申请

2、映射

3、访问

4、释放

申请I/O内存:内核提供了一套函数来允许驱动申请它需要的I/O内存,其中核心的函数是:

struct resource *request_mem_region(unsigned long start, unsigned long len, char *name)

这个函数申请一个从start开始,长度为len字节的内存区。如果成功,返回非NULL;否则返回NULL,所有已经在使用的I/O内存在/proc/iomem中列出。

映射I/O内存:在访问I/O内存之前,必须进行物理地址到虚拟地址的映射,ioremap函数具有此功能:

void *ioremap(unsigned long phys_addr, unsigned long size)

访问I/O内存:访问I/O内存的正确方法是通过一系列内核提供的函数:

从I/O内存读,使用下列之一:

unsigned ioread8(void *addr)

unsigned ioread16(void *addr)

unsigned ioread32(void *addr)

写I/O内存,使用下列之一:

void iowrite8(u8 value, void *addr)

void iowrite16(u16 value, void *addr)

void iowrite32(u32 value, void *addr)

老版本的I/O内存访问函数:

从I/O内存读,使用下列之一:

unsigned readb(address)

unsigned readw(address)

unsigned readl(address)

写I/O内存,使用下列之一:

unsigned writeb(unsigned value, address)

unsigned writew(unsigned value, address)

unsigned writel(unsigned value, address)

释放I/O内存

I/O内存不再需要使用时应该释放,步骤如下:

1、void iounmap(void * addr)

2、void release_mem_region(unsigned long start, unsigned long len)




对I/O空间的访问


 1、访问I/O内存(I/O内存必须映射到内存空间)的流程是:

  request_mem_region() -> ioremap() -> ioread8()/iowrite8() -> iounmap() -> release_mem_region() 。

  前面说过,IO内存是统一编址下的概念,对于统一编址,IO地址空间是物理主存的一部分,对于编程而言,我们只能操作虚拟内存,所以,访问的第一步就是要把设备所处的物理地址映射到虚拟地址,Linux2.6下用ioremap(): void *ioremap(unsigned long offset, unsigned long size); ioremap()用来将IO资源的物理地址映射到内核虚地址空间(3GB - 4GB)中,参数addr是指向内核虚地址的指针。然后,我们可以直接通过指针来访问这些地址,但是也可以用Linux内核的一组函数来读写: ioread8(), iowrite16(), ioread8_rep(), iowrite8_rep()......
 2、访问I/O端口

  访问IO端口有2种途径(对于独立编址系统体系):
  I/O映射方式(I/O-mapped)、内存映射方式(Memory-mapped)。

  前一种途径不映射到内存空间,直接使用 intb()/outb()之类的函数来读写IO端口;

  后一种MMIO是先把IO端口映射到IO内存(“内存空间”),再使用访问IO内存的函数来访问 IO端口。 void ioport_map(unsigned long port, unsigned int count); 通过这个函数,可以把port开始的count个连续的IO端口映射为一段“内存空间”,然后就可以在其返回的地址是像访问IO内存一样访问这些IO端口。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值