编程开发 购物 网址 游戏 小说 歌词 快照 开发 股票 美女 新闻 笑话 | 汉字 软件 日历 阅读 下载 图书馆 编程 China
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
移动开发 架构设计 编程语言 互联网 开发经验 Web前端 开发总结
开发杂谈 系统运维 研发管理 数据库 云 计 算 Java开发
VC(MFC) Delphi VB C++(C语言) C++ Builder 其它开发语言 云计算 Java开发 .Net开发 IOS开发 Android开发 PHP语言 JavaScript
ASP语言 HTML(CSS) HTML5 Apache MSSQL数据库 Oracle数据库 PowerBuilder Informatica 其它数据库 硬件及嵌入式开发 Linux开发资料
  编程开发知识库 -> 移动开发 -> Linux下直接读写物理地址内存 -> 正文阅读
 

[移动开发]Linux下直接读写物理地址内存[第1页]

虚拟 转 物理地址 
 virt_to_phys( *addr );
物理 转 虚拟地址 
 phys_to_virt( *addr );
-------------------------------------------------
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);
}

[cpp] view plain copy
外国项目中的一段代码:   if((fdMem = open("/dev/mem", O_RDWR | O_SYNC)) == -1)         {       fprintf(stderr, "Unable to open the /dev/mem interface !\n");       return;         }             map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fdMem, GPIO_ADDR & ~MAP_MASK);       if (map_base == (void *) -1)         {       fprintf(stderr, "Unable to map 0x%08x address\n", GPIO_ADDR);       close (fdMem);       return;         }          // Add offset for init (VD0) => PORTC8       virt_addr = map_base + 0x20;          // Configure GPIO       read_result  = *((unsigned long *) virt_addr);       read_result &= (~(0x03 << 16));       read_result |= 0x01 << 16;       *((unsigned long *) virt_addr) = read_result;          // Add offset for VD0       virt_addr = map_base + 0x24;  
阅读全文
本文已收录于以下专栏:

发表评论
HTML/XML objective-c Delphi Ruby PHP C# C++ JavaScript Visual Basic Python Java CSS SQL 其它
相关文章推荐
Linux下访问内存物理地址
Linux内核里提供的/dev/mem驱动,为我们读写内存物理地址,提供了一个渠道。下面讲述2种利用mem设备文件进行物理地址读写的方法,一种是 设备驱动的方法,另一种是系统调用的方法。首先我们看下m...
nancygreen 2011-06-20 18:14 591 Linux下访问内存物理地址
Linux内核里提供的/dev/mem驱动,为我们读写内存物理地址,提供了一个渠道。下面讲述2种利用mem设备文件进行物理地址读写的方法,一种是设备驱动的方法,另一种是系统调用的方法。 首先我们...
t9010 2011-08-02 09:04 382 Linux下访问内存物理地址
原文 Linux内核里提供的/dev/mem驱动,为我们读写内存物理地址,提供了一个渠道。下面讲述2种利用mem设备文件进行物理地址读写的方法,一种是设备驱动的方法,另一种是系统调用的方法。 ...
cjsycyl 2012-04-12 10:33 607 Zynq-Linux移植学习笔记之15-用户APP直接访问PL物理地址
1、  背景介绍 在zynq中,由于有PL部分的存在,操作系统需要对PL部分的物理地址进行操作,也就是对操作相关IP核的寄存器。除了在驱动中进行映射外(参看前一篇文章点击打开链接),也可以直接在...
jj12345jj198999 2017-06-16 21:49 542 linux下访问物理地址
Linux内核里提供的/dev/mem驱动,为我们读写内存物理地址,提供了一个渠道。下面讲述2种利用mem设备文件进行物理地址读写的方法,一种是设备驱动的方法,另一种是系统调用的方法。 首先我们...
cp25807720 2013-10-04 10:58 796 物理地址,虚拟内存,linux内存管理....
本贴涉及的硬件平台是X86,如果是其它平台,嘻嘻,不保证能一一对号入座,但是举一反三,我想是完全可行的。 一、概念 物理地址(physical address) 用于内存芯片级的单元寻址...
killcpp 2012-01-05 16:12 470 Linux内核-内存管理-PAE(物理地址扩展)
原文出自:http://blog.csdn.net/trochiluses/article/details/12853027 Intel 通过在处理器上把管脚数从 32 增加到 36,以提高处理器...
gd920129 2013-11-12 01:51 857 Linux下如何在进程中获取虚拟地址对应的物理地址
*如果让你编写一个程序,来获取虚拟地址对应的物理地址。。你会试着操作MMU吗。。→_→*
kongkongkkk 2017-07-05 02:04 287 Linux下获取虚拟地址对应的物理地址的方法
首先摘录如下URL对pagemap的描述。 https://www.kernel.org/doc/Documentation/vm/pagemap.txt  * /proc/pid/pagemap...
crazycoder8848 2014-08-31 09:07 8645 s5pv210 的中断向量表及中断在Linux下虚拟地址和物理地址的定义
在s5pv210下有四个中断向量表,ARM支持两种不同的中断分别是FIQ和IRQ。在s5pv210中在TrustZone Interrupt Controller提供的安全中断下一般FIQ,而普通的中...
zjc710268362 2015-04-17 15:40 892
yhc1991 +关注
原创 9 粉丝 7 喜欢 0 码云  
他的最新文章 更多文章
Vxworks启动参数详解 vxworks错误码解读 vx中的prjConfig.c winxp win7 删除 workbench
在线课程

【免费】搜狗机器翻译技术分享
讲师:

深度学习在推荐领域的应用和实践
讲师:吴岸城
热门文章 z301摄像头的驱动移植
2103
开发板的lcd 显示
2061
如何从linux内核--ubuntu系统cmdline上预留DDR物理内存DMA--mem=750M
1417
用Windows API 编写串口通讯程序(二)
1400
用Windows API 编写串口通讯程序(—)
1319
0
  移动开发 最新文章
Android设计模式之——状态模式
Android解决ScrollView默认不从顶部显示
关于在QtChart中绘出的图形进行点选的问题
Android Studio入门阶段的山路弯弯之Androi
Android设计模式之——责任链模式
DialogFragment实现自定义布局的小技巧
基于局部flush和全屏flush
android 使用binder实现Service与activity进
android性能优化实战理论篇
jenkins+maven+svn实现简单的一键发布
上一篇文章      下一篇文章      查看所有文章
加:2017-10-30 03:59:30  更:2017-10-30 03:59:34 
VC(MFC) Delphi VB C++(C语言) C++ Builder 其它开发语言 云计算 Java开发 .Net开发 IOS开发 Android开发 PHP语言 JavaScript
ASP语言 HTML(CSS) HTML5 Apache MSSQL数据库 Oracle数据库 PowerBuilder Informatica 其它数据库 硬件及嵌入式开发 Linux开发资料
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 新闻资讯 小游戏 Chinese Culture 股票 三丰软件 开发 中国文化 网文精选 阅读网 看图 日历 万年历 2018年11日历
2018-11-13 1:53:05
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  编程开发知识库