访问内存过程小结

     本文总结一下,Linux下面几种访问内存的方式方法。相关资料转载自:Linux 内存与I/O访问

     X86体系结构下,内存空间分为I/O空间和内存空间,I/O空间通过特定的指令in、out来访问,内存空间采用mov等指令访问。

     arm体系结构下,内存空间和I/O空间统一划分,他们在一个地址空间内。

      在处理器和真实的内存空间之间,还有MMU这一神奇的部件存在,它辅助操作系统进行内存管理,提供虚拟地址和物理地址的映射关系转换、内存访问权限保护和cache缓存控制等硬件功能,操作系统借助MMU,实现虚拟地址空间的管理,使得上层应用程序不必考虑真实物理地址大小,面对一片“虚拟”的逻辑地址空间。

    123

在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内容。

image

    按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 。

 

Technorati 标签:
原文地址:https://www.cnblogs.com/cherishui/p/4238690.html