LDD之IO端口与IO内存

一,IO端口与IO内存

         独立编址:处理器将IO地址在独立的IO地址空间编排(具有独立的操作指令,指令短访问速度快)——设备寄存器与设备内存被映射到IO地址空间称为IO映射;

         统一编址:处理器将IO地址和主内存在一个地址空间编排(具有统一的操作指令,操作内存的指令也可以操作设备寄存器与设备内存,操作种类多,指令长访问速度慢)————设备寄存器与设备内存被映射到内存空间称为内存映射;

       

        IO端口:设备寄存器与设备内存映射到IO地址空间称为IO端口;

        IO内存:设备寄存器与设备内存映射到物理内存地址空间称为IO内存;


        端口:处理器访问设备寄存器或设备内存的设备地址,端口也成为寄存器;由于端口<------>地址<-------->寄存器三者一一对应,所以寄存器地址即就是端口地址也成为端口;

   

二,IO端口与内存

       区别:IO端口访问具有边际效应,内存没有边际效应;

       IO端口 访问需要解决的问题:边际效应,优化(缓存,指令序列)

       优化问题的解决方案        :缓存优化——禁用缓存;

       指令序列优化的解决方案:设置内存屏障-达到避免指令序列被优化而重新排序的目的;

        linux内核实现的内存屏障函数:

        <linux/kernel.h>

          barrier();

          通知编译器添加内存屏障,对硬件没有任何影响,屏障前后的指令读写都是从内存中直接读取或写入,处理器对屏障前后的指令不进行优化重新排序;

         <asm/system.h>

         mb();        //读写内存屏障;

         wmb();     //写内存屏障;

         rmb();      //读内存屏障;

         read_barrier_depends();

         这几个内存屏障是添加硬件内存屏障,都是barrier()的超集;

          多处理版本:

         smp_mb();

         smp_wmb();

         smp_rmb();

         smp_read_barrier_depends(); 


 二,IO端口访问;

          Linux内核对IO端口访问提供了两种方式,这里是第一种:

          1,struct resources *request_region(unsigned long first_port , unsigned n, char *name);

                在对设备端口进行操作之前一定要取得设备的独占访问;

                这个函数是申请设备从first_port 端口开始的n个端口(地址),name为设备名称;

                first_port 为设备端口地址改地址是可以直接访问设备的物理地址;

                函数返回值为NULL则申请失败,非NULL则申请成功;

         2,IO端口操作函数:

                linux内核多端口的操作分为三类,按照端口的位宽来分;

                读取:

                unsigned char  inb(unsigned long port); //一个字节--8位端口;

                unsigned short inw(unsigned long port);//一个字长(双字节)--16位;

                unsigned inl(unsigned long port); //双子长(四个字节)--32位;      

               写入:

               void outb(char val,unsigned long port); 

               void outw(unsigned short val,unsigned long port);

               void outl(unsigned val,unsigned long port);

 

               串IO操作——对某个端口(读取或 )写入一个数据序列(数据序列的单位是一次能够读写的数据位宽)-与单次操作的区别:单次读取或写入可以调整字节序,而数据序列操作不能调整字节序:

                //in

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

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

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

                //out

                 void outsb(unsigned long port,void *addr,unsigned long count);//将地址addr处开始的count个字节写入端口;

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

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

          3,对设备端口独占访问释放:

                release_region(unsigned long first_port,unsigned long n);


三,IO内存访问;

        1,struct resources *request_mem_region(unsigned long addr,unsigned long size,char *name);

              申请从IO地址addr处开始的size大小的IO内存块;

              返回值为NULL申请失败,非NULL则申请成功;


              void *ioremap(unsigned long addr,unsigned long size);

              由于内核使用的是处理器的虚拟地址,所以将IO内存的物理地址映射到Linux内核虚拟地址空间中,便于Linux内核访问IO内存空间;

              无论是否存在MMU单元,为了代码的可移植性,都不要对函数返回的虚拟地址直接操作,而是要用Linux内核提供的安全的,经过优化的,平台统一的操作函数;

         2,IO内存操作函数:

               同IO端口一样分为三类:

               读取:

               unsigned char read8(void *addr);

               unsigned short read16(void *addr);

               unsigned read32(void *addr);

               写入:

               void write8(unsigned char val,void *addr);

               void write16(unsigned short val,void *addr);

               void write32(unsigned val,void *addr);


               数据序列操作--上述函数的重复版本(repeat):

               //read;

               void ioread8_rep(void *dest,void *source,unsigned long count);

               void ioread16_rep(void *dest,void *source,unsigned long count);

              void ioread32_rep(void *dest,void *source,unsigned long count);

               //write;

               void iowrite8_rep(void *dest,const void *source,unsigned long count);

               void iowrite16_rep(void *dest,const void *source,unsigned long count);

               void iowrite32_req(void *dest,const void *source,unsigned long count);

               将从原地址source开始的count个数据单位写入同一个目标地址dest处,数据单位是一次性写入IO内存的数据位宽;


       3,释放IO内存到内核虚拟地址空间的映射:

              void iounmap(void *addr,unsigned long count);

       4,释放IO内存独占访问:

             void release_mem_region(unsigned long addr, unsigned long size);


四,IO端口的第二种方位方式——采用和IO内存一样的操作函数——从操作层面淡化IO端口与IO内存之间的区别;

           

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值