访问内存过程小结
本文总结一下,Linux下面几种访问内存的方式方法。相关资料转载自:Linux 内存与I/O访问
X86体系结构下,内存空间分为I/O空间和内存空间,I/O空间通过特定的指令in、out来访问,内存空间采用mov等指令访问。
arm体系结构下,内存空间和I/O空间统一划分,他们在一个地址空间内。
在处理器和真实的内存空间之间,还有MMU这一神奇的部件存在,它辅助操作系统进行内存管理,提供虚拟地址和物理地址的映射关系转换、内存访问权限保护和cache缓存控制等硬件功能,操作系统借助MMU,实现虚拟地址空间的管理,使得上层应用程序不必考虑真实物理地址大小,面对一片“虚拟”的逻辑地址空间。
在Linux内核空间,kmalloc、__get_free_page,这两个函数申请的内存空间在物理地址上都是连续的,vmalloc申请的内存空间在物理地址上不是连续的。
方法1:
内核中,可以使用virt_to_phys()可以实现虚拟地址转换为物理地址,代码清单如下:
#define _ _pa(x) ((unsigned long)(x)-PAGE_OFFSET) extern inline unsigned long virt_to_phys(volatile void * address) { return _ _pa(address); }
与之对应的函数是phys_to_virt(),用于将物理地址转化为虚拟地址。具体代码如下:
#define _ _pa(x) ((unsigned long)(x)+PAGE_OFFSET)
extern inline unsigned long virt_to_phys(volatile void * address)
{
return _ _pa (address);
}
注意:上述方法仅适用于内核空间地址对于常规内存的访问,对于高端内存的访问,需要通过kmap和kunmap来动态映射访问。
方法2:
通过ioremap函数将设备所处的物理地址空间映射到虚拟地址空间
ioremap()原型:
void *ioremap(unsigned long offset, unsigned long size);
它返回一个特殊的虚拟地址,用来存取特定的物理地址页面,我们可以通过c指针来访问这些地址,
iounmap()原型:
void iounmap(void *addr);
它用来释放通过ioremap映射的虚拟地址空间
方法3:
通过mmap结合 /dev/mem 来直接映射物理内存空间,这种可以在应用程序态访问内核态的东西
/dev/mem 是物理内存的全镜像,可以用来访问物理I/O设备。通常只有root用户对其有读写权限
[Note]:新内核版本限制了/dev/mem中的内存访问接口,
/dev/kmem :kernel看到的虚拟内存全镜像,可以用来访问kernel的内容,查看kernel变量,用作rootkit等
对于/dev/mem,我们可以把他当做一个字符设备,先open,再read或者write就行。 也可以直接通过命令来查看和修改物理内存内容,这个命令就是hexedit
通过它,可以显示/dev/mem中的内容。执行 hexedit /dev/mem,显示结果如下:一行显示16个字节内容,从左到右分布是物理内存地址:4组十六进制内容,对应的ASCII内容。
按tab键进入修改模式,修改内容以粗体显示,按F2保存内容,按ctrl+c退出。
下面的函数需要 sys/mman.h 头文件支持。
1. void *mmap(void *stat, //映射结果地址,通常设为NULL,表示系统自动选择映射成功后的地址
size_t length, //映射大小,以字节为单位
int prot, // 映射区保护方式:可执行(PROT_EXEC),可读(PROT_READ),可写(PROT_WRITE)
int flag, // 特性选项:MAP_SHARED(对映射区域的写入写回到fd中) MAP_PRIVATE(对映射区域的写入不写回到fd)
int fd, // 指定映射的文件描述符(本例中,为由open以可读写的方式打开/dev/mem)
off_t offset // 被映射的偏移量 ,表示从哪个文件开始映射,一般设置为0,表示从头开始映射。offset必须为页大小的倍数(4K)
)
映射成功,返回对于的虚拟地址空间,映射失败,返回MAP_FAILED,同时errno错误变量被设置。
2. void munmap(void *addr,size_t length ) // 释放映射的虚拟内存空间 当调用进程终止时,该区域自动解除映射关系。关闭打开的文件描述符不会解除映射关系。
解除映射成功,munmap返回0,失败返回-1,全局errno被设置。
3. int msync(void *addr, //回写映射开始地址
size_t length, // 回写长度
int flags // MS_ASYNC: 异步调用,立即返回,回写操作由系统管理
// MS_SYNC:同步调用,阻塞,直到回写完成后才返回
)
msync将写入共享映射区的信息回写到磁盘上,不使用它,在munmap调用之前,无法确保写入映射区的内容真实写入磁盘中。
回写成功,返回0,失败返回-1,同时设置全局错误变量errno 。