访问I/O内存和I/O端口设备

前面为了写pwm驱动,仔细研究了下I/O内存和I/O端口设备的区别,以及访问方式。不过,其实也没必要纠结这个了,因为现在绝大部分设备都使用I/O内存映射的。

I/O独立编址和I/O统一编址

首先有两个概念:I/O独立编址和I/O统一编址;记住这两种编址方式都是由CPU架构决定的。

I/O独立编址:应该只有X86处理器才是I/O独立编址,其他的处理器基本都是统一编址了。所谓的I/O独立编址就是处理器上的所有物理地址(如果开启了MMU则为所有虚拟地址)都分配给总线和内存(可以理解为内存条上)。而 不 分配地址给外设,外设有自己的一套编址方式。所以有内存空间和I/O空间的叫法;

I/O统一编址:就是在分配地址时会留一部分地址给外设用。

内存空间和I/O空间

每种外设都是通过读写寄存器进行控制的,大部分外设都有几个寄存器,不管是在内存地址空间还是在I/O地址空间,这些寄存器的访问地址都是连续的。在硬件层,内存区域和I/O区域没有概念上的区别:他们都是通过向地址总线和控制总线发送电平信号进行访问,再通过数据总线读写数据;

上面简单的图说明了内存区域(memory)和I/O区域(I/O),他们都是通过地址总线(address)、控制总线(controller)、数据总线(data)发送信号来控制外设的。还有些处理器为I/O空间读写提供了独立的总线(上图是I/O空间没有独立的总线,和内存区域共用总线);

I/O端口映射和内存映射

I/O端口映射:这是在I/O独立编址处理器上映射的,把I/O端口映射到I/O空间,然后通过特殊指令去访问该空间的数据。系统会专门定义一些特殊指令来访问I/O空间:in、out;而系统也为这些指令封装成一些简单的函数:inb()、inw()、outb()、outw()。。。等;

内存映射:这是在统一编址的处理器上操作的。把外设寄存器和内存映射到系统的内存中,外设内存映射到系统内存中有点不一样,可以看看PCI设备,后期会分析下。

对外设的访问操作

端口映射访问:

头文件:#include <linux/ioport.h>

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

向内核注册需要使用的接口,first 使用端口的起始地址;n 注册n个端口;name 设备名称;可以从/proc/ioports中查看到端口分配的情况;

不再使用该I/O端口:void  release_region(unsigned long start,  unsigned long n);

#######################################################################################################

对端口的访问不能向内存那样使用一样的访问方式,硬件把8位、16位、32位端口区分开来;

头文件:  #include<asm/io.h>

unsigned inb(unsigned port);  =====  void outb(unsigned char byte, unsigned port);

unsigned inw(unsigned port); =====   void outw(unsinged short word, unsigned port);

内存映射访问:

头文件:#include<linux/ioport.h>

struct resource*  request_mem_region(unsigned long start, unsigned long len, char *name);表示从start开始分配len个字节长的内存区域。I/O内存分配情况可以从/proc/iomem中得到;

void  release_mem_region(unsigned long start, unsigned long len);释放接口;

######################################################################################################

注册完后,接下来就是映射到内存中和访问该段内存了。其实有的系统支持直接访问注册时得到的内存地址,但是这样不安全,也不利用代码移植,所以有些系统干脆就直接报错来阻止这种访问方式;

映射到内存中,通过下面函数:

void *ioremap(unsigned long phys_addr, unsigned long size);这个函数就是把虚拟地址和物理地址映射起来,一般会动用页表。这个后面linux内核内存会分析下。这里不一样的就是ioremap映射的物理地址不是系统的,而是外设的。释放映射:void iounmap(void *addr);

访问函数:

unsigned int ioread8(void *addr);

unsigned int ioread16(void *addr);

转载地址:http://blog.csdn.net/yuzhihui_no1/article/details/47111647

如果有理解不正确的地方,欢迎指正!!

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-08 00:12:02

访问I/O内存和I/O端口设备的相关文章

GNU C - 关于8086的内存访问机制以及内存对齐(memory alignment)

一.为什么需要内存对齐? 无论做什么事情,我都习惯性的问自己:为什么我要去做这件事情? 是啊,这可能也是个大家都会去想的问题, 因为我们都不能稀里糊涂的或者.那为什么需要内存对齐呢?这要从cpu的内存访问机制说起. 为了了解清楚cpu的内存访问机制,昨天整晚都在查找资料,但是还是找不到很好的介绍资料.后来只是找到了相关 的一些介绍的博客. 这些博客中大多都是以介绍内存对齐为主要目的,然后顺带着说一下cpu的内存访问机制,所以 找不到权威的资料,后来听说<<汇编语言编程艺术>>这本书

C# HttpBrowser 跨进程访问,解决内存泄露问题

1 #undef DEBUG 2 using Microsoft.Win32; 3 using Newtonsoft.Json; 4 using System; 5 using System.Collections.Generic; 6 using System.Collections.Specialized; 7 using System.Diagnostics; 8 using System.Diagnostics.Contracts; 9 using System.Drawing; 10

一个异步访问redis的内存问题

| 分类 redis? | 遇到一个redis实例突然内存飙高的案例, 具体症状如下: 客户端使用异步访问模式 单个请求的回包很大,hgetall一个8M的key 由于访问量比较大,已经登录不上redis了, 看不到具体在做什么做操, 因此使用perf来看下调用栈, 此处且按下不表. 为何内存会飙高呢,我们线下重现一下: import redis import time r=redis.Redis("127.0.0.1", 9988) pipe = r.pipeline() key=&

web api中访问数据库的内存释放问题

在使用web api开发微信后台的时候,本来想像MVC一样在controller中申明dbcontext全局变量,其它地方直接使用就可以了,结果调试过程中发现使用dbcontext访问数据库并获取query后,如果不使用asenumerable或者tolist强制断开与数据库的链接,那么非常容易出现out of system memory的错误,我觉得是内存一直没有释放的问题.后来我把dbcontext的全局变量去掉,在需要使用的时候使用using(var d = new dbcontext()

从外网访问局域网内的主机的方法(端口映射)

首先要做的事情如下: 1.确认你内网的路由器是否支持端口映射功能      2.如果你的路由器支持端口映射功能,在你本机安装远程控制软件      3.远程控制软件安装后,设置路由器,输入远程控制软件的端口和你本机ip,做端口映射      4.在你本机安装花生壳之类的动态域名软件      5.测试成功后,你在外网打开远程软件控制端,输入动态域名就可以访问你自己电脑了 端口映射(Port Mapping):      如果你是ADSL.MODEM或光纤等宽带接入用户,想在公司或单位内部建一个服

内存调试——valgrind工具对数组访问错误和内存泄漏的检测

下面的 C 程序分配了1024字节的内存,然后从分配的内存以外的区域读取数据,在分配内存尾部之后写数据,最后将该内存区域变得不可访问. #include <stdio.h> #include <stdlib.h> int main() { char *ptr = (char *)malloc( 1024 ); char ch; //Uninitialized read ch = ptr[1024]; //Write beyond the block ptr[1024] = 0; /

通过/dev/mem只能访问高端内存以下的内核线性地址空间

http://blog.chinaunix.net/uid-20564848-id-74706.html   </proc/iomem和/proc /ioports对应的fops> <浅析pc机上如何将vmlinuz- 2.6.31-14-generic解压出vmlinux> fs_initcall(chr_dev_init); chr_dev_init ==> register_chrdev(MEM_MAJOR,"mem",&memory_fop

开发环境Vue访问后端代码(前后端分离开发,端口不同下跨域访问)

开发环境下的跨域:在node.js上实现请求转发,前端请求到node.js上,node.js将请求转发到后端,反之.响应也是,先到node.js上,然后转发vue-cil项目上. 需要在根目录下新建文件vue.config.js. vue.config.js: vue.config.js 是一个可选的配置文件,如果项目的 (和 package.json 同级的) 根目录中存在这个文件,那么它会被 @vue/cli-service 自动加载.你也可以使用 package.json 中的 vue 字

访问固定的内存位置

要求设置一绝对地址为0x67a9的整型变量的值为0xaa66 第一种: int *ptr;     ptr = (int *)0x67a9;     *ptr = 0xaa66; 第二种: *(int * const)(0x67a9) = 0xaa66;