您的位置 首页 电源

linux中的IO端口映射和IO内存映射

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

  CPU地址空间

  (一)地址的概念

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

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

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

  物理地址与总线地址之间的关系由体系的规划决议的。在x86渠道上,物理地址便是总线地址,这是由于它们同享相同的地址空间——这句话有点难了解,详见下 面的“独立编址”。在其他渠道上,或许需求转化/映射。比方:CPU需求拜访物理地址是0xfa000的单元,那么在x86渠道上,会发生一个PCI总线 上对0xfa000地址的拜访。由于物理地址和总线地址相同,所以凭眼睛看是不能确认这个地址是用在哪儿的,它或许在内存中,或许是某个卡上的存储单元, 乃至或许这个地址上没有对应的存储器。

  3)虚拟地址:现代操作体系遍及选用虚拟内存办理(Virtual Memory Management)机制,这需求MMU(Memory Management 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从也表中来找到物理地址,再发生总线时序,完结取数据的。

  (二)编址办法

  1)外设都是经过读写设备上的寄存器来进行的,外设寄存器也称为“I/O端口”,而IO端口有两种编址办法:独立编址和一致编制。

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

  一致编址也称为“I/O内存”办法,外设寄存器坐落“内存空间”(许多外设有自己的内存、缓冲区,外设的寄存器和内存总称“I/O空间”)。

  如,Samsung的S3C2440,是32位ARM处理器,它的4GB地址空间被外设、RAM等分割:

  0x8000 1000 LED 8*8点阵的地址

  0x4800 0000 ~ 0x6000 0000 SFR(特别暂存器)地址空间

  0x3800 1002 键盘地址

  0x3000 0000 ~ 0x3400 0000 SDRAM空间

  0x2000 0020 ~ 0x2000 002e IDE

  0x1900 0300 CS8900

  独立编址(独自编址):IO地址与存储地址分隔独立编址,I/0端口地址不占用存储空间的地址规模,这样,在体系中就存在了另一种与存储地址无关的IO地 址,CPU也有必要具有专用与输入输出操作的IO指令(IN、OUT等)和操控逻辑。独立编址下,地址总线上过来一个地址,设备不知道是给IO端口的、仍是 给存储器的,所以处理器经过MEMR/MEMW和IOR/IOW两组操控信号来完成对I/O端口和存储器的不同寻址。如,intel 80×86就选用独自编址,CPU内存和I/O是一同编址的,便是说内存一部分的地址和I/O地址是堆叠的。

  独立编址也称为“I/O端口”办法,外设寄存器坐落“I/O(地址)空间”。

  关于x86架构来说,经过IN/OUT指令拜访。PC架构一共有65536个8bit的I/O端口,组成64K个I/O地址空间,编号从 0~0xFFFF,有16位,80×86用低16位地址线A0-A15来寻址。接连两个8bit的端口能够组成一个16bit的端口,接连4个组成一个 32bit的端口。I/O地址空间和CPU的物理地址空间是两个不同的概念,例如I/O地址空间为64K,一个32bit的CPU物理地址空间是4G。 如,在Intel 8086+Redhat9.0 下用“more /proc/ioports”可看到:

  0000-001f : dma1

  0020-003f : pic1

  0040-005f : timer

  0060-006f : keyboard

  0070-007f : rtc

  0080-008f : dma page reg

  00a0-00bf : pic2

  00c0-00df : dma2

  00f0-00ff : fpu

  0170-0177 : ide1

  ……

  不过Intel x86渠道一般运用了名为内存映射(MMIO)的技能,该技能是PCI标准的一部分,IO设备端口被映射到内存空间,映射后,CPU拜访IO端口就好像访 问内存相同。看Intel TA 719文档给出的x86/x64体系典型内存地址分配表:

  体系资源 占用

  BIOS 1M

  本地APIC 4K

  芯片组保存 2M

  IO APIC 4K

  PCI设备 256M

  PCI Express设备 256M

  PCI设备(可选) 256M

  显现帧缓存 16M

  TSEG 1M

  关于某一既定的体系,它要么是独立编址、要么是一致编址,详细选用哪一种则取决于CPU的体系结构。 如,PowerPC、m68k等选用一致编址,而X86等则选用独立编址,存在IO空间的概念。现在,大多数嵌入式微操控器如ARM、PowerPC等并 不供给I/O空间,仅有内存空间,可直接用地址、指针拜访。但关于Linux内核而言,它或许用于不同的CPU,所以它有必要都要考虑这两种办法,所以它采 用一种新的办法,将根据I/O映射办法的或内存映射办法的I/O端口通称为“I/O区域”(I/O region),不管你选用哪种办法,都要先恳求IO区域:request_resource(),结束时开释 它:release_resource()。

  2)对外设的拜访

  1、拜访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);

  然后,咱们能够直接经过指针来拜访这些地址,可是也能够用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端口。

  Linux下的IO端口和IO内存

  CPU对外设端口物理地址的编址办法有两种:一种是IO映射办法,另一种是内存映射办法。

  Linux将根据IO映射办法的和内存映射办法的IO端口总称为IO区域(IO region)。

  IO region依然是一种IO资源,因而它依然能够用resource结构类型来描绘。

  Linux办理IO region:

  1) request_region()

  把一个给定区间的IO端口分配给一个IO设备。

  2) check_region()

  查看一个给定区间的IO端口是否闲暇,或许其间一些是否现已分配给某个IO设备。

  3) release_region()

  开释曾经分配给一个IO设备的给定区间的IO端口。

  Linux中能够经过以下辅佐函数来拜访IO端口:

  inb(),inw(),inl(),outb(),outw(),outl()

  “b”“w”“l”别离代表8位,16位,32位。

  对IO内存资源的拜访

  1) request_mem_region()

  恳求分配指定的IO内存资源。

  2) check_mem_region()

  查看指定的IO内存资源是否已被占用。

  3) release_mem_region()

  开释指定的IO内存资源。

  其间传给函数的start address参数是内存区的物理地址(以上函数参数表已省掉)。

  驱动开发人员能够将内存映射办法的IO端口和外设内存一致看作是IO内存资源。

  ioremap()用来将IO资源的物理地址映射到内核虚地址空间(3GB – 4GB)中,参数addr是指向内核虚地址的指针。

  Linux中能够经过以下辅佐函数来拜访IO内存资源:

  readb(),readw(),readl(),writeb(),writew(),writel()。

  Linux在kernel/resource.c文件中界说了全局变量ioport_resource和iomem_resource,来别离描绘根据IO映射办法的整个IO端口空间和根据内存映射办法的IO内存资源空间(包含IO端口和外设内存)。

  1)关于IO与内存空间:

  在X86处理器中存在着I/O空间的概念,I/O空间是相关于内存空间而言的,它经过特定的指令in、out来拜访。端口号标识了外设的寄存器地址。Intel语法的in、out指令格局为:

  IN 累加器, {端口号│DX}

  OUT {端口号│DX},累加器

  现在,大多数嵌入式微操控器如ARM、PowerPC等中并不供给I/O空间,而仅存在内存空间。内存空间能够直接经过地址、指针来拜访,程序和程序运转中运用的变量和其他数据都存在于内存空间中。

  即便是在X86处理器中,尽管供给了I/O空间,假如由咱们自己规划电路板,外设依然能够只挂接在内存空间。此刻,CPU能够像拜访一个内存单元那样拜访外设I/O端口,而不需求建立专门的I/O指令。因而,内存空间是有必要的,而I/O空间是可选的。

  (2)inb和outb:

  在Linux设备驱动中,宜运用Linux内核供给的函数来拜访定坐落I/O空间的端口,这些函数包含:

  · 读写字节端口(8位宽)

  unsigned inb(unsigned port);

  void outb(unsigned char byte, unsigned port);

  · 读写字端口(16位宽)

  unsigned inw(unsigned port);

  void outw(unsigned short word, unsigned port);

  · 读写长字端口(32位宽)

  unsigned inl(unsigned port);

  void outl(unsigned longword, unsigned port);

  · 读写一串字节

  void insb(unsigned port, void *addr, unsigned long count);

  void outsb(unsigned port, void *addr, unsigned long count);

  · insb()从端口port开端读count个字节端口,并将读取成果写入addr指向的内存;outsb()将addr指向的内存的count个字节接连地写入port开端的端口。

  · 读写一串字

  void insw(unsigned port, void *addr, unsigned long count);

  void outsw(unsigned port, void *addr, unsigned long count);

  · 读写一串长字

  void insl(unsigned port, void *addr, unsigned long count);

  void outsl(unsigned port, void *addr, unsigned long count);

  上述各函数中I/O端口号port的类型高度依赖于详细的硬件渠道,因而,仅仅写出了unsigned。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/dianyuan/90137.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部