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. MySQL 时间类型字段的分析

    日期类型                存储空间               日期格式                                           日期范围---------- ...

  2. JAVA 几种多线程的简单实例 Thread Runnable

    实例1: class Hello extends Thread{ private String name; public Hello(){} public Hello(String name){ th ...

  3. Oracle 数据块损坏与恢复具体解释

    1.什么是块损坏: 所谓损坏的数据块,是指块没有採用可识别的 Oracle 格式,或者其内容在内部不一致. 通常情况下,损坏是由硬件故障或操作系统问题引起的.Oracle 数据库将损坏的块标识为&qu ...

  4. Running the app on your device

    So far, you've run the app on the Simulator. That's nice and all but probably notwhy you're learning ...

  5. Qt5.8 提供 Apple tvOS,watchOS的技术预览版

    New Platforms Apple tvOS (technology preview) Apple watchOS (technology preview) https://wiki.qt.io/ ...

  6. 如何让 ssh 允许以 root 身份登录

    默认情况下,Pack 上的 root 用户不能用通过密码来远程登录,可以用一下命令来做:(注意要在 root 权限下) sed -i 's/PermitRootLogin\swithout-passw ...

  7. 辨异 —— Java 中的抽象类和接口

    接口优于抽象类.-- <Effective Java>(Item 18) 0. 语法区别 抽象类允许给出某些方法的实现,接口不允许: 为了实现由抽象类定义的类型(type),类必须成为抽象 ...

  8. 【NOIP 2002】 字串变换

    [题目链接] https://www.luogu.org/problemnew/show/P1032 [算法] 广度优先搜索 用stl库里的map来判重 [代码] #include<bits/s ...

  9. 【POJ 2417】 Discrete Logging

    [题目链接] http://poj.org/problem?id=2417 [算法] Baby-Step,Giant-Step算法 [代码] #include <algorithm> #i ...

  10. Spring MVC简介 2.5 Spring MVC执行的流程

    package org.fkit.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http ...