驱动程序中物理页式地址映射程序设计怎样对文件映射

you have been blocked虚拟地址空间映射到物理地址空间_网络基础知识
虚拟地址空间映射到物理地址空间
【网络基础知识】 学习啦编辑:春健
本文已影响 人
  学习啦小编整理了虚拟地址空间映射到地址空间的相关资料。供大家参考!
  虚拟地址空间映射到物理地址空间参考如下
  当处理器读或写入位置时,它会使用虚拟地址。作为读或写操作的一部分,处理器将虚拟地址转换为物理地址。通过虚拟地址访问内存有以下优势:
  程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。
  程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。
  不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程或使用的物理内存。
  进程可用的虚拟地址范围称为该进程的&虚拟地址空间&。每个用户模式进程都有其各自的专用虚拟地址空间。 对于 32 位进程,虚拟地址空间通常为 2 GB,范围从 0x 至 0x7FFFFFFF。对于 64 位进程,虚拟地址空间为 8 TB,范围从 0x000' 至 0x7FF'FFFFFFFF。一系列虚拟地址有时称为一系列&虚拟内存&。
  此图说明了虚拟地址空间的一些重要功能。
  该图显示了两个 64 位进程的虚拟地址空间:Notepad.exe 和 MyApp.exe。每个进程都有其各自的虚拟地址空间,范围从 0x000'0000000 至 0x7FF'FFFFFFFF。每个阴影框都表示虚拟内存或物理内存的一个页面(大小为 4 KB)。注意,Notepad 进程使用从 0x7F7' 开始的虚拟地址的三个相邻页面。但虚拟地址的这三个相邻页面会映射到物理内存中的非相邻页面。而且还注意,两个进程都使用从 0x7F7' 开始的虚拟内存页面,但这些虚拟页面都映射到物理内存的不同页面。
  用户空间和系统空间
  诸如 Notepad.exe 和 MyApp.exe 的进程在用户模式下运行。核心操作系统组件和多个驱动程序在更有特权的内核模式下运行。有关处理器模式的详细信息,请参阅用户模式和内核模式。每个用户模式进程都有其各自的专用虚拟地址空间,但在内核模式下运行的所有代码都共享称为&系统空间&的单个虚拟地址空间。当前用户模式进程的虚拟地址空间称为&用户空间&。
  在 32 位 Windows 中,可用的虚拟地址空间共计为 2^32 字节(4 GB)。通常较下的 2 GB 用于用户空间,较上的 2 GB 用于系统空间。
  在 32 位 Windows 中,你可以指定(在启动时)超过 2 GB 用于用户空间。结果是系统空间可用的虚拟地址更少。可以将用户空间的大小增至 3 GB,在这种情形下系统空间仅有 1 GB。若要增大用户空间的大小,请使用 BCDEdit /set increaseuserva。
  在 64 位 Windows 中,虚拟地址空间的理论大小为 2^64 字节(16 百亿亿字节),但实际上仅使用 16 百亿亿字节范围的一小部分。范围从 0x000' 至 0x7FF'FFFFFFFF 的 8 TB 用于用户空间,范围从 0xFFFF00000 至 0xFFFFFFFF'FFFFFFFF 的 248 TB 的部分用于系统空间。
  用户模式下运行的代码可以访问用户空间,但不能访问系统空间。此限制可防止用户模式代码读或更改受保护的操作系统数据结构。内核模式下运行的代码既可以访问用户空间,也可以访问系统空间。即,在内核模式下运行的代码可以访问系统空间和当前用户模式进程的虚拟地址空间。
  在内核模式下运行的驱动程序必须在直接从用户空间地址中读取或写入这些地址时非常小心。此方案说明了原因。
  用户模式程序发起从设备读取某些数据的请求。程序提供缓冲区的起始地址以接收数据。
  在内核模式下运行的设备驱动程序例程启动读取操作并将控制权返回到其调用程序。
  然后,设备中断了当前运行的任何线程以显示读取操作完成。 中断由在此任意线程上运行的内核模式驱动程序例程进行处理,该例程属于任意进程。
  此时,驱动程序不得将数据写入用户模式程序在步骤 1 中提供的开始地址。此地址位于发起请求的进程的虚拟地址空间,该进程可能很大程度上不同于当前进程。
  虚拟地址(Virtual Address Space)
  Win32通过一个两层的表结构来实现地址映射,因为每个进程都拥有私有的4G的虚拟内存空间,相应的,每个进程都有自己的层次表结构来实现其地址映射。
  第一层称为页目录,实际就是一个内存页,Win32的内存页有4KB大小,这个内存页以4个字节分为1024项,每一项称为&页目录项&(PDE);
  第二层称为页表,这一层共有1024个页表,页表结构与页目录相似,每个页表也都是一个内存页,这个内存页以4KB的大小被分为1024项,页表的每一项被称为页表项(PTE),易知共有个页表项。每一个页表项对应一个物理内存中的某一个&内存页&,即共有个物理内存页,每个物理内存页为4KB,这样就可以索引到4G大小的虚拟物理内存。
  如下图所示(注下图中的页目录项的大小应该是4个字节,而不是4kB):
  Win32提供了4GB大小的虚拟地址空间。因此每个虚拟地址都是一个32位的整数值,也就是我们平时所说的指针,即指针的大小为4B。它由三部分组成,如下图:
  这三个部分的第一部分,即前10位为页目录下标,用来寻址页目录项,页目录项刚好1024个。找到页目录项后,找对页目录项对应的的页表。第二部分则是用来在页表内寻址,用来找到页表项,共有1024个页表项,通过页表项找到物理内存页。第三部分用来在物理内存页中找到对应的字节,一个页的大小是4KB,12位刚好可以满足寻址要求。
  具体的例子:
  假设一个线程正在访问一个指针(Win32的指针指的就是虚拟地址)指向的数据,此指针指为0x2A8E317F,下图表示了这一个过程:
  0x2A8E317F的二进制写法为__,为了方便我们把它分为三个部分。
  首先按照寻址,找到页目录项。因为一个页目录项为4KB,那么先将左移两位,(0x2A8),用此下标找到页目录项,然后根据此页目录项定位到下一层的某个页表。
  然后按照寻址,在上一步找到页表中寻找页表项。寻址方法与上述方法类似。找到页表项后,就可以找到对应的物理内存页。
  最后按照寻址,寻找页内偏移。
  上面的假设的是此数据已在物理内存中,其实判断访问的数据是否在内存中也是在地址映射过程中完成的。Win32系统总是假设数据已在物理内存中,并进行地址映射。页表项中有一位标志位,用来标识包含此数据的页是否在物理内存中,如果在的话,就直接做地址映射,否则,抛出缺页中断,此时页表项也可标识包含此数据的页是否在调页文件中(外存),如果不在则访问违例,程序将会退出,如果在,页表项会查出此数据页在哪个调页文件中,然后将此数据页调入物理内存,再继续进行地址映射。为了实现每个进程拥有私有4G的虚拟地址空间,也就是说每个进程都拥有自己的页目录和页表结构,对不同进程而言,即使是相同的指针(虚拟地址)被不同的进程映射到的物理地址也是不同的,这也意味着在进程之间传递指针是没有意义的。
  看过&虚拟地址空间映射到物理地址空间 &的人还看了:
[虚拟地址空间映射到物理地址空间]相关的文章
看过本文的人还看了
834人看了觉得好
742人看了觉得好
779人看了觉得好
【网络基础知识】图文推荐
Copyright & 2006 -
All Rights Reserved
学习啦 版权所有linux物理地址的静态映射 - 推酷
linux物理地址的静态映射
早先看linux驱动相关书籍的时候,隐约记得物理地址有动态映射和静态映射,当时写驱动都是想着自己动手写,所以一上手看着动态映射好用,就在自己的驱动上使用动态映射,将寄存器地址映射到内存上,即调用ioremap函数。在单线作战的时候ioremap确实好用,不需要改动内核的其他部分,直接将自己想用的物理地址映射到内存再操作。但很多时候当我们的设备需要在全局范围内被使用的时候,ioremap就会比较尴尬,其他c文件想要操作该设备的虚拟地址就会比较麻烦,而静态映射就会方便很多。
最近这两天在写一个蜂鸣器的驱动,这一次我尝试放弃单线作战,使用系统提供的蜂鸣器驱动程序(driver/input/misc/pwm-beeper.c),尝试理解内核驱动程序的思想。
在mach-smdk4x12.c文件添加平台设备
static struct platform_device samsung_bp_device = {
.name= &pwm-beeper&,
.parent = &s3c_device_timer[1].dev,
.platform_data = 1,
使用platform_device_register函数将设备注册进入平台总线,这样子,在驱动程序pwm-beeper.c通过总线获取到对应的设备信息。通常来讲,只要驱动程序通过platform总线成功获取到beeper设备的信息,驱动基本上就可以使用了。在这个驱动中,蜂鸣器对应的PWM管脚要设置成相应的功能,比如在4412中将GPIOD0_1设置成功能2,即PWM输出。这样子写一个应用程序测试,蜂鸣器基本上就可以用了。但是我在看蜂鸣器驱动pwm-beeper.c的过程中,没有看到有关物理地址映射的函数,我们都知道内核是无法直接读写物理地址的,一路追踪驱动中对地址的操作,最终在plat-samsung/include/plat/map-base.h看到#define S3C_ADDR_BASE
0xF6000000,PWM的地址是以S3C_ADDR_BASE为基址的一个偏移地址,咋一看蜂鸣器的驱动完全是在对一个define定义的立即数进行读写,那么这个define定义的地址到底是物理地址还是虚拟地址。从datasheet上来看PWM寄存器地址是0x139D0000,那么这个地址就应该是虚拟地址了,这引起了我对物理地址的静态映射的兴趣,因为静态映射之后的虚拟地址使用起来太方便了。下面描述一下物理地址的静态映射,因为涉及到MMU、页表等比较复杂的东西,所以只能粗略的讲讲。
物理地址的静态映射在系统启动过程中完成,在mach-exynos/common.c文件中可以看到静态映射的过程。以S3C_VA_TIMER为例将定义的虚拟地址、实际物理地址、申请地址大小和地址类型填入map_desc结构体中。
static struct map_desc exynos4_iodesc[] __initdata = {
.virtual= (unsigned long)S3C_VA_TIMER,
.pfn= __phys_to_pfn(EXYNOS4_PA_TIMER),
.length= SZ_16K,
.type= MT_DEVICE,
填充好的map_desc结构体最终通过函数iotable_init,将物理地址与虚拟地址的对应关系加到MMU页表中,页表中有了虚拟地址和物理地址的对应关系,这样虚拟地址能最终映射到实际的物理地址上,静态映射完成。
iotable_init(exynos4_iodesc, ARRAY_SIZE(exynos4_iodesc));
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致字符设备驱动(13)
micro2440采用S3C2440处理器,和这个平台相关的代码主要在arch/arm/mach-s3c2410和include/asm-arm/arch-s3c2410中,相关驱动在drivers目录中。
(1)DM9000 网卡驱动
Linux-2.6.32.2/drivers/net/dm9000.c
(2)串口(包括三个串口驱动0,1,2,对应设备名/dev/ttySAC0,1,2)
Linux-2.6.32.2/drivers/serial/s3c2440.c
(3)实时时钟RTC 驱动
Linux-2.6.32.2/drivers/rtc/rtc-s3c.c
(4)LED 驱动
Linux-2.6.32.2/drivers/char/mini2440_leds.c
(5)按键驱动
Linux-2.6.32.2/drivers/char/mini2440_buttons.c
(6)触摸屏驱动
Linux-2.6.32.2/drivers/input/touchscreen/s3c2410_ts.c
(7)yaffs2 文件系统源代码目录
Linux-2.6.32.2/fs/yaffs2
(8)USB 鼠标、键盘源代码
Linux-2.6.32.2/drivers/usb/hid
(9)SD/MMC 卡驱动源代码目录(支持高速最大容量32G SD 卡)
Linux-2.6.32.2/drivers/mmc
(10)Nand Flash 驱动
Linux-2.6.32.2/drivers/mtd/nand
(11)UDA1341 音频驱动目录
Linux-2.6.32.2/sound/soc/s3c24xx
(12)LCD 驱动
Linux-2.6.32.2/drivers/video/s3c2410fb.c
(13)优盘支持驱动
Linux-2.6.32.2/drivers/usb/storage
(14)万能USB 摄像头驱动
Linux-2.6.32.2/drivers/media/video/gspca
(15)I2C-EEPROM 驱动
inux-2.6.32.2/drivers/i2c
(16)背光驱动
Linux-2.6.32.2/drivers/video/mini2440_backlight.c
(17)PWM 控制蜂鸣器驱动
Linux-2.6.32.2/drivers/char/mini2440_pwm.c
(18)看门狗驱动
Linux-2.6.32.2/drivers/watchdog/s3c2410_wdt.c
(19)AD 转换驱动
Linux-2.6.32.2/drivers/char/mini2440_ad.c
(20)CMOS 摄像头驱动
Linux-2.6.32.2/drivers/media/video/s3c2440camif.c
(21)USB 无线网卡驱动(型号:TL-WN321G+)
Linux-2.6.32.2/drivers/net/wireless/rt2x00
(22)USB 转串口驱动
Linux-2.6.32.2/drivers/usb/serial/pl2302.c
1.S3C2410_GPB5是端口编号,定义在regs-gpio.h中,
搜索所在位置:
[root@localhost linux-2.6.32.2_fa]# find ./ -name &regs-gpio.h&&
./arch/arm/mach-s3c2410/include/mach/regs-gpio.h
#define S3C2410_GPIO_BANKB & (32*1)
#define S3C2410_GPIONO(bank,offset) & ((bank) + (offset))
#define S3C2410_GPB5 & & & & S3C2410_GPIONO(S3C2410_GPIO_BANKB, 5)
上面的S3C2410_GPB5就是GPIO的编号,也就是在号码空间(0~32*9-1)中的位置,bank是分组的基号码,offset是组内偏移量。
2.S3C2410_GPB5_OUTP是端口功能,定义在regs-gpio.h中,
#define S3C2410_GPB5_INP & & (0x00 && 10)
#define S3C2410_GPB5_OUTP & &(0x01 && 10)
GPBCON的第10、11两位用于配置GPB5的功能,00 = Input ,01 = Output
3.S3C2410 GPIO的操作函数
在hardware.h文件中有:
s3c2410_gpio_cfgpin & & //配置端口的GPIO的功能,输入,输出等
s3c2410_gpio_getcfg & & //读取功能配置
s3c2410_gpio_pullup & & //配置上拉电阻
s3c2410_modify_misccr //杂项配置
s3c2410_gpio_getirq & & &//给定端口,转换出IRQ号
s3c2410_gpio_irqfilter & &//配置IRQ过滤使能与否
s3c2410_gpio_setpin & & //写数据到端口,写0,或者写1
s3c2410_gpio_getpin & & //从端口读数据
这些函数的实现在gpio.h中
void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
void __iomem *base = S3C2410_GPIO_BASE(pin);//虚拟基地址
unsigned long offs = S3C2410_GPIO_OFFSET(pin);//偏移量
local_irq_save(flags);
dat = __raw_readl(base + 0x04);
dat &= ~(1 && offs);
dat |= to &&
__raw_writel(dat, base + 0x04);
local_irq_restore(flags);
(自己的理解:dat = __raw_readl(base + 0x04); dat &= ~(1 && offs); dat |= to &&譬如操作的是端口5,这几句能保证其他的端口保持原值,而仅仅是端口5值改变)
4.S3C2410_GPIO_BASE和S3C2410_GPIO_OFFSET也是在regs-gpio.h文件中定义,
#define S3C2410_GPIO_BASE(pin)
((((pin) & ~31) && 1) + S3C24XX_VA_GPIO)
#define S3C2410_GPIO_OFFSET(pin)
((pin) & 31)
而在map.h中有:
#define S3C24XX_VA_GPIO
S3C2410_ADDR(0x00E00000) //虚拟地址S3C24XX_VA_GPIO= 0xF0E00000
#define S3C2400_PA_GPIO
#define S3C2410_PA_GPIO
(0x) //GPACON 物理地址
#define S3C24XX_SZ_GPIO
SZ_1M //0x100000 =
S3C2410_GPIO_BASE作用是:根据端口编号pin,算出端口所在组的虚拟基址。((pin) & ~31)是去掉pin当中小于等于31的零头(清0低5位),&&1的原因是每组GPIO中最多可以有32个端口,控制这些端口需要4个寄存器空间,4个寄存器空间就需要4*4=16个字节进行编址,32/16=2,左移一位刚好满足。也就是说,上一组端口和下一组端口的编号相差32,而控制寄存器的地址相差16。(自己的理解:因为每个GPIO口对应4个寄存器,每个寄存器32位,S3C2410_GPIO_BASE仅仅是算出虚拟基址,而不管是哪个具体端口,2的5次方正好是32,((pin)
& ~31)可以屏蔽掉低五位,右移一位是因为端口数不超过32,每个GPIO口对应4个寄存器,每个寄存器32位,4*32/8=16=0x10,例如GPIOA=0X10+S3C24XX_VA_GPIO ,GPIOB=0X20+S3C24XX_VA_GPIO,0x10所表示的数的个数超过32)
S3C2410_GPIO_OFFSET作用是:根据端口编号pin,算出端口所在组的偏移量。((pin) & 31)即去掉比31大的数(清0第6位以上的位)。
5. __raw_readl和__raw_writel
Linux对I/O的操作都定义在asm/io.h中,相应的在arm平台下,就在asm-arm/io.h中。
#define __raw_readl(a) & (__chk_io_ptr(a), *(volatile unsigned int __force & *)(a))
#define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force & *)(a) = (v))
在include\linux\compiler.h中:
#ifdef __CHECKER__
extern void __chk_io_ptr(void __iomem *);
# define __chk_io_ptr(x) (void)0
__raw_readl(a)展开是:((void)0, *(volatile unsigned int _force *)(a))。在定义了__CHECKER__的时候先调用__chk_io_ptr检查该地址,否则__chk_io_ptr什么也不做,*(volatile unsigned int _force *)(a)就是返回地址为a处的值。(void)xx的做法有时候是有用的,例如编译器打开了检查未使用的参数的时候需要将没有用到的参数这么弄一下才能编译通过。
CPU对I/O的物理地址的编程方式有两种:一种是I/O映射,一种是内存映射。__raw_readl和__raw_writel等是原始的操作I/O的方法,由此派生出来的操作方法有:inb、outb、_memcpy_fromio、readb、writeb、ioread8、iowrite8等。
6.local_irq_save和local_irq_restore
关中断和开中断,在asm-arm/system.h中定义。
#define local_irq_save(x)
__asm__ __volatile__(
&mrs %0, cpsr
@ local_irq_save\n& \
: &=r& (x) : : &memory&, &cc&);
#define local_irq_save(x)
(void) (&temp == &x);
__asm__ __volatile__(
&mrs %0, cpsr
@ local_irq_save\n& \
& orr %1, %0, #128\n&
& msr cpsr_c, %1&
: &=r& (x), &=r& (temp)
: &memory&, &cc&);
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:48901次
排名:千里之外
原创:43篇
转载:20篇
(23)(32)(8)you have been blocked

我要回帖

更多关于 页式地址映射程序设计 的文章

 

随机推荐