文章目录
1. 什么是虚拟地址
所谓虚拟地址映射就是从虚拟地址映射到物理地址,MMU开启后,CPU访问的地址都是虚拟地址。不光linux采用虚拟地址,windows等其他操作系统也采用虚拟地址而不直接使用物理地址。
2. 为什么要使用虚拟地址
直接使用物理地址的存在的问题:
- 安全风险。每个进程都可以访问0-4G的任意的内存空间,这也就意味着任意一个进程都能够去读写系统相关内存区域,如果是一个 木马病毒,那么他就能随意的修改内存空间,让设备直接瘫痪。
- 地址不确定。众所周知,编译完成后的程序是存放在硬盘上的,当运行的时候,需要将程序搬到内存当中去运行,如果直接使用物理地址的话,我们无法确定内存现在使用到哪里了,也就是说拷贝的实际内存地址每一次运行都是不确定的,比如:第一次执行a.out时候,内存当中一个进程都没有运行,所以搬移到内存地址是0x00000000,但是第二次的时候,内存已经有10个进程在运行了,那执行a.out的时候,内存地址就不一定了。
- 效率低下。如果直接使用物理内存的话,一个进程就是作为一个整体(内存块)操作的,如果出现物理内存不够用的时候,我们一般的办法是将不常用的进程拷贝到磁盘的交换分区中,好腾出内存,但是如果是物理地址的话,就需要将整个进程一起拷走,这样,在内存和磁盘之间拷贝时间太长,效率较低。
3. 两种映射方式
虚拟地址映射分为
静态映射和
动态映射。
3-1. 静态映射
所谓的映射表其实是头文件中的宏定义。
- 在内核启动时建立静态映射表,在内核关机时销毁,中间一直有效,优点是执行效率高,缺点是始终占用虚拟地址空间,空间利用率低。
- 不同版本内核静态映射表位置,文件名可能不同。
- 不同的SOC静态映射表位置,文件名可能不同。
使用方法:
地址映射表中虚拟地址定义如下
#define S5PV210_GPJ0CON 0xFD050240
#define S5PV210_GPJ0DAT 0xFD050244
在驱动程序中使用
#define GPJ0CON S5PV210_GPJ0CON
#define GPJ0DAT S5PV210_GPJ0DAT
#define rGPJ0CON*((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT*((volatile unsigned int *)GPJ0DAT)
rGPJ0CON = 0x11111111;
rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));
rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
3-2. 动态映射
驱动程序根据需要随时动态建立使用和销毁映射,映射是短期临时的。
使用方法:
#define GPJ0CON_PA 0xE0200240
#define GPJ0DAT_PA 0xE0200244
unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;
if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON"))
return -EINVAL;
if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0CON"))
return -EINVAL;
pGPJ0CON = ioremap(GPJ0CON_PA, 4);
pGPJ0DAT = ioremap(GPJ0DAT_PA, 4);
*pGPJ0CON = 0x11111111;
*pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));
*pGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
iounmap(pGPJ0CON);
iounmap(pGPJ0DAT);
release_mem_region(GPJ0CON_PA, 4);
release_mem_region(GPJ0DAT_PA, 4);
3-3. 如何选择两种映射方法
- 两种映射并不排他,可以同时使用。
- 静态映射类似于C语言中全局变量,动态方式类似于C语言中malloc堆内存。
- 静态映射的好处是执行效率高,坏处是始终占用虚拟地址空间;动态映射的好处是按需使用虚拟地址空间,坏处是每次使用前后都需要代码去建立映射&销毁映射(还得学会使用那些内核函数的使用)。