1,内存空间与IO空间

  1)I/O 映射方式(I/O-mapped) 典型地,如X86处理器为外设专门实现了一个单独的地址空间,称为"I/O地址空间"或者"I/O端口空间",CPU通过专门的I/O指令(如X86的IN和OUT指令)来访问这一空间中的地址单元。

  2)内存映射方式(Memory-mapped)   RISC指令系统的CPU(如ARM、PowerPC等)通常只实现一个物理地址空间(不提供IO空间),外设I/O端口成为内存的一部分。此时,CPU可以象访问一个内存单元那样访问外设I/O端口,而不需要设立专门的外设I/O指令。

  X86处理器虽然提供了IO空间,但是我们仍然可以挂接在内存空间中,CPU就可以像访问内存单元那样方法外设IO端口,不需要专门的IO指令,因此内存空间是必须的,而IO空间是可有可无的。

2,内存管理单元(MMU)

MMU辅助操作系统进行内存管理,提供虚拟地址和物理地址映射、内存访问权限保护和Cache缓存控制等硬件支持。

TLB(Translation Lookaside Buffer)转换旁路缓存,缓存少量的虚拟地址与物理地址的转换关系,是转换表的Cache,被称为“块表”;

TTW(Translation Table walk)转换表漫游,当TLB中没有想要的地址转换关系时,则需要对内存中的转换表(为多级页表)的访问获得虚拟地址和物理地址的对应关系,TTW成功后,结构应写入TLB。

MMU具有的虚拟地址和物理地址映射、内存访问权限保护等功能,使得Linux系统能单独为系统的每个用户进程分配独立的内存空间并保证用户空间不能访问内核空间的地址,为操作系统的虚拟内存管理模块提供了硬件基础。

3,linux内存管理

  对于包含MMU的处理器,linux系统提供复杂的存储管理系统,使得进程所能访问的内存到达4GB。

  在linux中,进程的4GB内存空间被分为两部分,用户空间(0~3G)和内核空间(3~4G),用户进程只有通过系统调用才可以访问内核空间,不能直接访问。

  每个进程的用户空间是完全独立的,用户进程各自有不同的页表。而内核空间不会跟着进程改变,是固定的。内核空间的虚拟地址到物理地址映射是被所有进程共享的,内核的虚拟地址独立于其他程序。

  linux中1GB的内核地址空间又被分为物理内存映射区,虚拟内存分配区,高端页面映射区,专用页面映射区,系统保留区。

4,内存的存取

1)用户空间内存动态申请

用户空间动态申请内存函数为malloc(),对应的释放是free(),linux系统中C库的malloc函数一般通过brk和mmap两个系统调用从内核申请内存。

2)内核空间内存的动态申请

kmalloc()和_ _get_free_pages() 申请的内存位于上面说的物理内存映射区,而且物理上是连续的,他们与真实的物理地址只有一个固定的偏移。

vmalloc()在虚拟空间给一块连续内存区,在物理上不一定连续,且申请的虚拟地内存和物理内存没有简单的转换关系。

void *kmalloc(size_t size, int flags);

kmalloc对应kfree()释放内存,kmalloc()底层是用_ _get_free_pages() 实现的,_ _get_free_pages() 是linux内核最底层用于获取空闲内存的方法。

5,设备IO端口和IO内存的访问流程

  各种硬件设备通常会提供一组寄存器来控制、读写设备和获取设备状态,即控制寄存器,数据寄存器和状态寄存器。这些寄存器如果位于IO空间则被称为IO端口,当位于内存空间时,对应的内存被称为IO内存。

1)IO端口的两种访问流程

 2)IO内存的访问流程

两种访问方式的例子(来自https://blog.csdn.net/dndxhej/article/details/8374205):

利用IO Port方式

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h> #include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h>
#include <linux/ioport.h> #include <mach/regs-gpio.h>
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_*_user */
#include <asm/io.h> #define LED_NUM 4 struct led_dev
{
struct cdev dev;
unsigned port;
unsigned long offset;
}; struct led_dev led[];
dev_t dev = ;
static struct resource *led_resource; int led_open(struct inode *inode, struct file *filp)
{
struct led_dev *led; /* device information */ led = container_of(inode->i_cdev, struct led_dev, dev);
filp->private_data = led; /* for other methods */ return ; /* success */
} int led_release(struct inode *inode, struct file *filp)
{
return ;
} ssize_t led_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
return ;
} ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
char data;
struct led_dev *led;
u32 value;
printk(KERN_INFO "debug by baikal: led dev write\n"); led = (struct led_dev *)filp->private_data;
copy_from_user(&data,buf,count);
if(data == '')
{
printk(KERN_INFO "debug by baikal: led off\n");
value = inl((unsigned)(S3C2410_GPBDAT));
outl(value | <<led->offset,(unsigned)(S3C2410_GPBDAT));
//value = ioread32(led->base);
//iowrite32( value | 1<<led->offset, led->base);
}
else
{
printk(KERN_INFO "debug by baikal: led on\n");
value = inl((unsigned)(S3C2410_GPBDAT));
outl(value & ~(<<led->offset),(unsigned)(S3C2410_GPBDAT));
//value = ioread32(led->base);
//iowrite32( value & ~(1<<led->offset), led->base);
}
} struct file_operations led_fops = {
.owner = THIS_MODULE,
.read = led_read,
.write = led_write,
//.ioctl = led_ioctl,
.open = led_open,
.release = led_release,
}; static int led_init(void)
{
int result, i; result = alloc_chrdev_region(&dev, , LED_NUM,"LED");
if (result < ) {
printk(KERN_WARNING "LED: can't get major %d\n", MAJOR(dev));
return result;
}
led_resource = request_region(0x56000014,0x4,"led");
if(led_resource == NULL)
{
printk(KERN_ERR " Unable to register LED I/O addresses\n");
return -;
}
for(i = ; i < LED_NUM; i++)
{
cdev_init( &led[i].dev, &led_fops);
//led[i].port = ioport_map(0x56000014,0x4);
//led[i].base = ioremap(0x56000014,0x4);
led[i].offset = i + ; //leds GPB5\6\7\8
led[i].dev.owner = THIS_MODULE;
led[i].dev.ops = &led_fops;
result = cdev_add(&led[i].dev,MKDEV(MAJOR(dev),i),);
if(result < )
{
printk(KERN_ERR "LED: can't add led%d\n",i);
return result;
}
} return ;
} static void led_exit(void)
{
int i;
release_region(0x56000014,0x4);
for( i = ; i < LED_NUM; i++)
{
//iounmap(led[i].base); cdev_del(&led[i].dev);
}
unregister_chrdev_region(dev, LED_NUM); } module_init(led_init);
module_exit(led_exit); MODULE_AUTHOR("Baikal");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple LED Driver");

利用IO Mem方式

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h> #include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h>
#include <linux/ioport.h> #include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_*_user */
#include <asm/io.h> #define LED_NUM 4 struct led_dev
{
struct cdev dev;
void __iomem *base;
unsigned long offset;
}; struct led_dev led[];
dev_t dev = ; int led_open(struct inode *inode, struct file *filp)
{
struct led_dev *led; /* device information */ led = container_of(inode->i_cdev, struct led_dev, dev);
filp->private_data = led; /* for other methods */ return ; /* success */
} int led_release(struct inode *inode, struct file *filp)
{
return ;
} ssize_t led_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
return ;
} ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
char data;
struct led_dev *led;
u32 value;
printk(KERN_INFO "debug by baikal: led dev write\n"); led = (struct led_dev *)filp->private_data;
copy_from_user(&data,buf,count);
if(data == '')
{
printk(KERN_INFO "debug by baikal: led off\n");
value = ioread32(led->base);
iowrite32( value | <<led->offset, led->base);
}
else
{
printk(KERN_INFO "debug by baikal: led on\n");
value = ioread32(led->base);
iowrite32( value & ~(<<led->offset), led->base);
}
} struct file_operations led_fops = {
.owner = THIS_MODULE,
.read = led_read,
.write = led_write,
//.ioctl = led_ioctl,
.open = led_open,
.release = led_release,
}; static int led_init(void)
{
int result, i; result = alloc_chrdev_region(&dev, , LED_NUM,"LED");
if (result < ) {
printk(KERN_WARNING "LED: can't get major %d\n", MAJOR(dev));
return result;
} for(i = ; i < LED_NUM; i++)
{
cdev_init( &led[i].dev, &led_fops);
request_mem_region(0x56000014,0x4,"led");
led[i].base = ioremap(0x56000014,0x4);
led[i].offset = i + ; //leds GPB5\6\7\8
led[i].dev.owner = THIS_MODULE;
led[i].dev.ops = &led_fops;
result = cdev_add(&led[i].dev,MKDEV(MAJOR(dev),i),);
if(result < )
{
printk(KERN_ERR "LED: can't add led%d\n",i);
return result;
}
} return ;
} static void led_exit(void)
{
int i;
release_mem_region(0x56000014,0x4);
for( i = ; i < LED_NUM; i++)
{
iounmap(led[i].base); cdev_del(&led[i].dev);
}
unregister_chrdev_region(dev, LED_NUM); } module_init(led_init);
module_exit(led_exit); MODULE_AUTHOR("Baikal");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple LED Driver");

更多I/O操作及代码分析参考

http://www.cnblogs.com/reality-soul/p/6126376.html

https://blog.csdn.net/ce123_zhouwei/article/details/7204458

驱动中的IO访问的更多相关文章

  1. Linux设备驱动中的IO模型---阻塞和非阻塞IO【转】

    在前面学习网络编程时,曾经学过I/O模型 Linux 系统应用编程——网络编程(I/O模型),下面学习一下I/O模型在设备驱动中的应用. 回顾一下在Unix/Linux下共有五种I/O模型,分别是: ...

  2. Linux驱动设计——内存与IO访问

    名词解释 内存空间与IO空间 内存空间是计算机系统里面非系统内存区域的地址空间,现在的通用X86体系提供32位地址,寻址4G字节的内存空间,但一般的计算机只安装256M字节或者更少的内存,剩下的高位内 ...

  3. 七、设备驱动中的阻塞与非阻塞 IO(一)

    7.1 阻塞与非阻塞 IO 阻塞操作是指在执行设备操作的时候,若不能获取资源,则挂起进程直到满足可操作的条件后再进行操作.被挂起的进程进入睡眠状态,被从调度器的运行队列移走,直到等待的条件被满足. 非 ...

  4. 七、设备驱动中的阻塞与非阻塞 IO(二)

    7.2 轮询 7.2.1 介绍 在用户程序中的 select() 和 poll() 函数最终会使设备驱动中的 poll() 函数被执行. 设备驱动程序中的轮询函数原型: /** 用于询问设备是否可以非 ...

  5. 10、驱动中的阻塞与非阻塞IO

        阻塞,就是在获取资源的时候,不能获取到,那么就会将当前的进程挂起(睡眠,也就是将当前进程从调度器拿走了,不会调度当前进程),直到满足条件为止再进行操作.相反,非阻塞,就是即使不能获取到资源,非 ...

  6. 深入理解JAVA I/O系列六:Linux中的IO模型

    IO模型 linux系统IO分为内核准备数据和将数据从内核拷贝到用户空间两个阶段. 这张图大致描述了数据从外部磁盘向运行中程序的内存中移动的过程. 用户空间.内核空间 现在操作系统都是采用虚拟存储器, ...

  7. Linux驱动设计—— 内外存访问

    本节对内外存访问做详细的介绍. 驱动程序加载成功的一个关键因素,就是内核能够为驱动程序分配足够的内存空间.这些空间一部分用于驱动程序必要的数据结构,另一部分用于数据的交换.同时,内核也应该具有访问外部 ...

  8. Linux设备驱动中的异步通知与异步I/O

    异步通知概念: 异步通知的意识是,一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上的“中断”概念,比较准确的称谓是“信号驱动的异步IO”,信号是在软件层次 ...

  9. Linux设备驱动中的阻塞和非阻塞I/O

    [基本概念] 1.阻塞 阻塞操作是指在执行设备操作时,托不能获得资源,则挂起进程直到满足操作所需的条件后再进行操作.被挂起的进程进入休眠状态(不占用cpu资源),从调度器的运行队列转移到等待队列,直到 ...

随机推荐

  1. UVa 10290 - {Sum+=i++} to Reach N

    题目:给你一个数字问将他写成连续的数字的和的形式.有几种写法. 分析:数论. 设拆成的序列个数为k,我们分两种情况讨论: 1.拆成奇数个连续数.那么设中位数是a,则有n = k * a: 2.拆成偶数 ...

  2. LNMP 架构 上传文件

    修改PHP上传文件大小限制的方法   修改PHP上传文件大小限制的方法1. 一般的文件上传,除非文件很小.就像一个5M的文件,很可能要超过一分钟才能上传完.但在php中,默认的该页最久执行时间为 30 ...

  3. 常用框架(一):spring+springMvc+mybatis+maven

    项目说明: (1) 本例采用 maven web 工程做例子讲解 (2) 利用mybaits 提供的代码生成工具自动生成代码(dao接口,sql mapper映射文件,pojo数据库映射类) (3) ...

  4. xcode打包测试

    模拟器的内存cpu网络,都是电脑的.xcode可以查看. Xcode7之前是限制人,限制电脑,限制app,限制真机调试的. Xcode7之后,做真机测试只需要apple id即可,会自动生成证书. X ...

  5. 使用getopt命令解析shell脚本的命令行选项 【转】

    本文转载自:http://yejinxin.github.io/parse-shell-options-with-getopt-command 在之前的一篇文章中,介绍了如何利用shell内置的get ...

  6. day63-webservice 07.07.如何修改cxf配置文件路径

    为什么第一次访问http://localhost:8080/cxf-web-server/service有点慢?证明第一次访问的时候CXFServlet被初始化了被加载了.一般是让CXFServlet ...

  7. 爬虫之Urllib库的基本使用

    官方文档地址:https://docs.python.org/3/library/urllib.html 什么是Urllib Urllib是python内置的HTTP请求库包括以下模块urllib.r ...

  8. lua环境变量

    function foo() print(g or "'g' is not defined!") end foo() env = { g = 100, print = print ...

  9. 如何修改 WordPress 数据库前缀

    我们知道 WordPress 的数据库表,可以设置前缀,默认是 wp_,很多同学也就默认用了 wp_,如果某种原因(比如提高安全性)要修改的 WordPress 数据的前缀,我们应该怎么做? 开始之前 ...

  10. switch注意事项

    Day03_SHJavaTraining_4-5-2017 switch注意事项:①switch语句接受的数据类型 switch语句中的表达式的数据类型,是有要求的 JDK1.0 - 1.4    数 ...