虚拟 转 物理地址  virt_to_phys( *addr );
物理 转 虚拟地址  phys_to_virt( *addr );

如:

    unsigned long pProtectVA;
phys_addr_t ProtectPA; gM4U_ProtectVA = pProtectVA;
ProtectPA = virt_to_phys((void *)pProtectVA);

-------------------------------------------------
Linux内核里提供的/dev/mem驱动,为我们读写内存物理地址,提供了一个渠道。下面讲述2种利用mem设备文件进行物理地址读写的方法,一种是设备驱动的方法,另一种是系统调用的方法。

首先我们看下mem这个设备文件,/dev/mem是linux下的一个字符设备,源文件是~/drivers/char/mem.c,这个设备文件是专门用来读写物理地址用的。里面的内容是所有物理内存的地址以及内容信息。通常只有root用户对其有读写权限。

1.设备驱动的方法

下面是mem.c文件里定义的file_operations结构,提供了llseek,read,write,mmap以及open等方法。

static struct file_operations mem_fops =
{
       .llseek   = memory_lseek,
       .read   = read_mem,
       .write   = write_mem,
       .mmap   = mmap_mem,
       .open   = open_mem,
};

因此我们可以通过一般驱动的使用方法,将内存完全当作一个设备来对对待。应用程序如下:

#include <stdio.h>
#include <fcntl.h>
int main(void)
{
       int fd;
       char *rdbuf;
       char *wrbuf = "butterfly";
       int i;
       fd = open("/dev/mem",O_RDWR);
       if(fd < 0)
       {
         printf("open /dev/mem failed.");
       }
       read(fd,rdbuf,10);

for(i = 0;i < 10;i++)
       {
         printf("old mem[%d]:%c\n",i,*(rdbuf + i));
       }
       lseek(fd,5,0);
       write(fd,wrbuf,10);
       lseek(fd,0,0);//move f_ops to the front
       read(fd,rdbuf,10);
       for(i = 0;i < 10;i++)
       {
         printf("new mem[%d]:%c\n",i,*(rdbuf + i));
       }

return 0;
}

执行结果如下:将内存最开始10个字节的内容进行替换。

[root@VOIP-IPCAM app]# ./memtest
old mem[0]:b
old mem[1]:u
old mem[2]:t
old mem[3]:t
old mem[4]:e
old mem[5]:r
old mem[6]:f
old mem[7]:l
old mem[8]:y
old mem[9]:!
new mem[0]:b
new mem[1]:u
new mem[2]:t
new mem[3]:t
new mem[4]:e
new mem[5]:b
new mem[6]:u
new mem[7]:t
new mem[8]:t
new mem[9]:e

2.系统调用的方法

细心的你可能会发现,既然你前面说了这个文件里存放的就是内存的地址及内容信息,那我可不可以直接查看到呢,答案是:可以的。linux内核的开发者为我 们提供了一个命令hexedit,通过它就可以将/dev/mem的内容显示出来(如果你使用cat /dev/mem将会看到乱码),执行hexedit /dev/mem的结果如下:

00000000 62 75 74 74   65 62 75 74   74 65 72 66   6C 79 21 20   butterfly!
00000010 20 20 20 20   20 20 20 20   20 20 20 20   20 20 20 20
00000020 20 20 20 20   20 20 20 20   20 20 20 20   20 20 20 20
00000030 6F EF 00 F0   6F EF 00 F0   57 EF 00 F0   6F EF 00 F0   o...o...W...o...
00000040 02 11 00 C0   4D F8 00 F0   41 F8 00 F0   34 85 00 F0   ....M...A...4...
00000050 39 E7 00 F0   59 F8 00 F0   2E E8 00 F0   D2 EF 00 F0   9...Y...........
00000060 A4 E7 00 F0   F2 E6 00 F0   6E FE 00 F0   53 FF 00 F0   ........n...S...
00000070 53 FF 00 F0   A4 F0 00 F0   C7 EF 00 F0   1C 42 00 C0   S............B..

从上图可见,最左边显示的是地址,接下来24列显示的是各内存字节单元内容的ASCII码信息,最右边显示的是对应的字符信息。让人欣慰的是,这个文件可 以直接修改,按下tab键进入修改模式,修改过程中修改内容会以粗体显示,按下F2保存后粗体消失。上面的butterfly就是通过这种方式修改的。

既然内存的地址以及内容信息全部被保存在mem这个设备文件里,那么我们可以想到通过另外一种方式来实现对物理地址的读写了。那就是将mem设备文件和 mmap系统调用结合起来使用,将文件里的物理内存地址映射到进程的地址空间,从而实现对内存物理地址的读写。下面谈一下mmap系统调用。

mmap的函数原型为:void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offset),该函数定义在/usr/include/sys/mman.h中,使用时要包含:#include<sys /mman.h>,mmap()用来将某个文件中的内容映射到进程的地址空间,对该空间的存取即是对该文件内容的读写。参数说明如下:

start:指向欲映射到的地址空间的起始地址,通常设为null或者0.表示让系统融自动选定地址,映射成功后该地址会返回。

length:表示映射的文件内容的大小,以字节为单位。

prot:表示映射区域的保护方式,有如下四种组合:
--PROT_EXEC 映射区域可执行 ,
--PROT_READ 映射区域可读 ,
--PROT_WRITE 映射区域可写,
--PROT_NONE 映射区域不能被访问

flags:映射区域的一些特性,主要有:
--MAP_FIXED 如果映射不成功则出错返回,
--MAP_SHARED 对映射区域的写入数据会写回到原来的文件
--MAP_PRIVATE 对映射区域的写入数据不会写回原来的文件
--MAP_ANONYMOUS
--MAP_DENYWRITE 只允许对映射区域的写入操作,其他对文件直接写入的操作将被拒绝
--MAP_LOCKED 锁定映射区域

在调用mmap()时,必须要指定MAP_SHARED或MAP_PRIVATE。

fd:open()返回的文件描述符。
offset:为被映射文件的偏移量,表示从文件的哪个地方开始映射,一般设置为0,表示从文件的最开始位置开始映射。offset必须是分页大小(4096字节)的整数倍。

应用程序如下:

#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>//mmap head file
int main (void)
{
   int i;
   int fd;
   char *start;
   char *buf = "butterfly!";

//open /dev/mem with read and write mode
   fd = open ("/dev/mem", O_RDWR);
   if (fd < 0)
   {
          printf("cannot open /dev/mem.");
          return -1;
   }

//map physical memory 0-10 bytes
   start = (char *)mmap(0, 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
   if(start < 0)
   {
      printf("mmap failed.");
      return -1;
   }
   //Read old value
   for (i = 0; i < 10; i++)
   {
          printf("old mem[%d]:%c\n", i, *(start + i));
   }
   //write memory
   memcpy(start, buf, 10);
   //Read new value
   for (i = 0;i < 10;i++)
   {
      printf("new mem[%d]:%c\n", i,*(start + i));
   }
   munmap(start, 10); //destroy map memory
   close(fd);   //close file
   return 0;
}

程序执行结果如下:

[root@VOIP-IPCAM app]# ./rwphy
old mem[0]:b
old mem[1]:u
old mem[2]:t
old mem[3]:t
old mem[4]:e
old mem[5]:b
old mem[6]:u
old mem[7]:t
old mem[8]:t
old mem[9]:e
new mem[0]:b
new mem[1]:u
new mem[2]:t
new mem[3]:t
new mem[4]:e
new mem[5]:r
new mem[6]:f
new mem[7]:l
new mem[8]:y
new mem[9]:!

“/dev/mem是个很好玩的东西,你竟然可以直接访问物理内存。这在Linux下简直是太神奇了,这种感觉象一个小偷打算偷一个银行,可是这个银行戒备森严,正当这个小偷苦无对策时,突然发现在一个不起眼的地方有个后门,这个后门可以直接到银行的金库。”

Linux下/dev/mem和/dev/kmem的区别:
/dev/mem: 物理内存的全镜像。可以用来访问物理内存。
/dev/kmem: kernel看到的虚拟内存的全镜像。可以用来访问kernel的内容。
作用:
 /dev/mem用来访问物理IO设备,比如X用来访问显卡的物理内存,或嵌入式中访问GPIO。用法一般就是open,然后mmap,接着可以使用map之后的地址来访问物理内存。这   其实就是实现用户空间驱动的一种方法。
/dev/kmem后者一般可以用来查看kernel的变量,或者用作rootkit之类的。参考1和2描述了用来查看kernel变量这个问题。

/dev/mem用法举例:
比如驱动(内核空间)中
vir_addr = kmalloc( size, GFP_KERNEL | GFP_DMA )
 phy_addr = __pa( vir_addr );
申请了一段内存,然后得到__pa()得到它的物理地址,然后将该物理地址传到用户空间
然后在应用程序(用户空间)中
 int map_fd = open("/dev/mem", O_RDWR);
 map_addr = mmap(0,  size,  PROT_READ|PROT_WRITE, MAP_SHARED,  map_fd,  phy_addr);

这样就可以得到 在应用程序中 访问驱动程序中申请的内存的指针map_addr,可以实现应用程序和驱动程序访问同段内存,节省开销,实现内存共享

这是一种方法,把内核空间的内存映射到用户空间,内核空间内存-->物理地址(PA)-->用户空间  通过/dev/mem 和系统调用mmap

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h> #include <sys/types.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <sys/mman.h> int page_size;
#define PAGE_SIZE page_size
#define PAGE_MASK (~(PAGE_SIZE-1)) void get_var (unsigned long addr) {
off_t ptr = addr & ~(PAGE_MASK);
off_t offset = addr & PAGE_MASK;
int i = 0;
char *map;
static int kfd = -1; kfd = open("/dev/kmem",O_RDONLY);
if (kfd < 0) {
perror("open");
exit(0);
} map = mmap(NULL,PAGE_SIZE,PROT_READ,MAP_SHARED,kfd,offset);
if (map == MAP_FAILED) {
perror("mmap");
exit(-1);
}
printf("%s\n",map+ptr); return;
} int main(int argc, char **argv)
{
FILE *fp;
char addr_str[11]="0x";
char var[51];
unsigned long addr;
char ch;
int r; if (argc != 2) {
fprintf(stderr,"usage: %s System.map\n",argv[0]);
exit(-1);
} if ((fp = fopen(argv[1],"r")) == NULL) {
perror("fopen");
exit(-1);
} do {
r = fscanf(fp,"%8s %c %50s\n",&addr_str[2],&ch,var);
if (strcmp(var,"modprobe_path")==0)
break;
} while(r > 0);
if (r < 0) {
printf("could not find modprobe_path\n");
exit(-1);
}
page_size = getpagesize();
addr = strtoul(addr_str,NULL,16);
printf("found modprobe_path at (%s) %08lx\n",addr_str,addr);
get_var(addr);
}
  1. 外国项目中的一段代码:
  2. if((fdMem = open("/dev/mem", O_RDWR | O_SYNC)) == -1)
  3. {
  4. fprintf(stderr, "Unable to open the /dev/mem interface !\n");
  5. return;
  6. }
  7. map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fdMem, GPIO_ADDR & ~MAP_MASK);
  8. if (map_base == (void *) -1)
  9. {
  10. fprintf(stderr, "Unable to map 0x%08x address\n", GPIO_ADDR);
  11. close (fdMem);
  12. return;
  13. }
  14. // Add offset for init (VD0) => PORTC8
  15. virt_addr = map_base + 0x20;
  16. // Configure GPIO
  17. read_result  = *((unsigned long *) virt_addr);
  18. read_result &= (~(0x03 << 16));
  19. read_result |= 0x01 << 16;
  20. *((unsigned long *) virt_addr) = read_result;
  21. // Add offset for VD0
  22. virt_addr = map_base + 0x24;

Linux下直接读写物理地址内存的更多相关文章

  1. linux下的CPU、内存、IO、网络的压力测试

    linux下的CPU.内存.IO.网络的压力测试  要远程测试其实很简单了,把结果放到一个微服务里直接在web里查看就可以了,或者同步到其他服务器上 一.对CPU进行简单测试: 1.通过bc命令计算特 ...

  2. [转帖]linux下的CPU、内存、IO、网络的压力测试

    linux下的CPU.内存.IO.网络的压力测试 https://www.cnblogs.com/zhuochong/p/10185881.html 一.对CPU进行简单测试: 1.通过bc命令计算特 ...

  3. Linux下C程序的内存映像

    2.Linux下C程序的内存映像 2.1. 代码段.只读数据段(1)对应着程序中的代码(函数),代码段在Linux中又叫文本段(.text)(2)只读数据段就是在程序运行期间只能读不能写的数据,con ...

  4. linux下对进程按照内存使用情况进行排序

    linux下对进程按照内存使用情况进行排序的命令为:ps aux --sort -rss 详细解说参见 http://alvinalexander.com/linux/unix-linux-proce ...

  5. 浅析Linux下进程间通信:共享内存

    浅析Linux下进程间通信:共享内存 共享内存允许两个或多个进程共享一给定的存储区.因为数据不需要在客户进程和服务器进程之间复制,所以它是最快的一种IPC.使用共享内存要注意的是,多个进程之间对一给定 ...

  6. Linux下jmap命令查看内存使用

    Linux下jmap命令查看内存使用 jmap -heap 1234(1234为进程号) jmap是JDK自带的一个工具,非常小巧方便,其支持参数如下: -heap       打印heap空间的概要 ...

  7. Linux下如何释放cache内存

    转自:http://www.linuxidc.com/Linux/2015-02/112685.htm 在Linux下频繁存取文件后,物理内存会很快被用光,当程序结束后,内存不会被正常释放,而是一直作 ...

  8. Linux下查看进程占用内存的最好方式

    今天看到stackoverflow上关于linux下如何查看某个进程占用的内存是多少的回答,觉得非常棒,不过是全英文的,很多人可能看不懂,所以我翻译一下 翻译自http://stackoverflow ...

  9. 【转】Linux 下取进程占用 cpu/内存 最高的前10个进程

    # Linux 下 取进程占用 cpu 最高的前10个进程ps aux|head -1;ps aux|grep -v PID|sort -rn -k +3|head # linux 下 取进程占用内存 ...

随机推荐

  1. 2017 [六省联考] T5 分手是祝愿

    4872: [Shoi2017]分手是祝愿 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 458  Solved: 299[Submit][Statu ...

  2. 转 Windows串口过滤驱动程序的开发

    在Windows系统上与安全软件相关的驱动开发过程中,“过滤(filter)”是极其重要的一个概念.过滤是在不影响上层和下层接口的情况下,在Windows系统内核中加入新的层,从而不需要修改上层的软件 ...

  3. Android动画系列 - PropertyAnim 详解

    前言:上一篇文章传统View动画与Property动画基础及比较简单对Android动画系统的基础做了介绍,本篇文章将对PropertyAnimation进行全面深入的探讨,本篇文章可以分为两大块,从 ...

  4. WEBLOGIC启动后,重启后控制台进入缓慢、延迟,探查WEBLOGIC

    本文说的是解决过程,可直接点击本行略过探查内容,跳到最后的解决办法!! 现象: 1.WEBLOGIC安装在 CENTOSopenSUSE 等LINUX或者UNIX机器上,无论是虚拟机或者PC或者服务器 ...

  5. SQLAlchemy的查询操作Query

    查询操作 查询子句使用session的.query()方法来获取Query查询对象.查询对象能够使用一些方法来对应一些查询子句,比如.order_by(),.limit(),.filter()等. 查 ...

  6. 智能手机+DIY红外=万能遥控器

    目前好像只有:三星S4.,努比亚大牛,华为荣耀3等几款新机才有红外遥控功能,那我们使用的手机没有这个功能怎么办?不要急我有办法呵呵,本次DIY材料好找又简单,大家都可以亲自试一试! DIY材料:红外二 ...

  7. 判断用户Input输入的事件来进行登陆

    我们是通过键盘按的object.keyCode获取的 Html <input onkeydown="keydownMsg(event)" type="text&qu ...

  8. codefoeces B. Friends and Presents

    B. Friends and Presents time limit per test 1 second memory limit per test 256 megabytes input stand ...

  9. Food hub

    Work center List Tillage 耕作 Hand harvest 手工采收 Planting 种植 Cultivating 培养 Mulching 覆盖 Dig harvest 挖地采 ...

  10. SMI#、SCI#信号在OS、BIOS、EC中的中断方式(Linux)

    EC资料有个很好的CSDN博客推荐去看看:zhao_longwei的专栏 EC固件代码架构:https://blog.csdn.net/zhao_longwei/article/details/510 ...