Linux 如何使用/dev/kmem?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10800884/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to use /dev/kmem?
提问by Tom Xue
Updated my post...
更新了我的帖子...
I got below program. It operates on /dev/kmem
and /dev/mem
.
我得到了下面的程序。它在/dev/kmem
和 上运行/dev/mem
。
I think I can learn something from the code. But when I run it on my Beagle Board, below result is given:
我想我可以从代码中学到一些东西。但是当我在我的 Beagle Board 上运行它时,给出了以下结果:
case 1: ( if(1) )
root@omap:/home/ubuntu/tom# ./kmem_mem /boot/System.map-3.0.4-x3
found jiffies at (0xc0870080) c0870080
/dev/kmem read buf = 319317
jiffies=319317 (read from virtual memory)
/dev/mem: the offset is 870080
the page size = 4096
mmap: Invalid argument
case 2: ( if(0) )
root@omap:/home/ubuntu/tom# ./kmem_mem /boot/System.map-3.0.4-x3
found jiffies at (0xc0870080) c0870080
/dev/kmem read buf = 333631
jiffies=333631 (read from virtual memory)
/dev/mem: the offset is 870080
/dev/mem read failed: Bad address
jiffies=0 (read from physical memory)
And I used below command so that mmap can use NULL as its first parameter.
我使用了下面的命令,以便 mmap 可以使用 NULL 作为其第一个参数。
root@omap:/home/ubuntu/tom# echo 0 > /proc/sys/vm/mmap_min_addr
root@omap:/home/ubuntu/tom# cat /proc/sys/vm/mmap_min_addr
0
As you can see, read_kmem()
works fine but read_mem()
doesn't work, and it seems that the 'offset' transferred to it is wrong. But kernel address - PAGE_OFFSET(0xC0000000) = physical address
, is it wrong?
如您所见,read_kmem()
工作正常但read_mem()
不起作用,并且转移到它的“偏移量”似乎是错误的。但是内核地址 - PAGE_OFFSET(0xC0000000) = physical address
,有错吗?
My questions are:
(1) Why "mmap: Invalid argument" in case 1?
(2) Why the mmap only maps PAGE_SIZE
length space?
(3) What's wrong with read_mem
?
我的问题是:(1)为什么在情况 1 中“mmap:无效参数”?(2)为什么mmap只映射PAGE_SIZE
长度空间?(3) 怎么了read_mem
?
Can anyone help? Thanks!
任何人都可以帮忙吗?谢谢!
/*
* getjiff.c
*
* this toolkit shows how to get jiffies value from user space:
* 1. find jiffies's address from kernel image.
* 2. access virtual address space to get jiffies value.
* 3. access physical address sapce to get jiffies value.
*
* demostrate following techniques:
* o get ELF object symbol address by calling nlist()
* o access virtual memory space from /dev/kmem
* o access virtual memory space from /dev/mem
*/
#include <stdio.h>
#include <stdlib.h> //exit
#include <linux/a.out.h> //nlist
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <memory.h>
#define LONG *(volatile unsigned long*)
/* read from virtual memory */
int read_kmem(off_t offset, void* buf, size_t count)
{
int fd;
int n;
fd = open("/dev/kmem", O_RDONLY);
if (fd < 0)
{
perror("open /dev/kmem failed");
return -1;
}
lseek(fd, offset, SEEK_SET);
n = read(fd, buf, count);
if (n != count)
perror("/dev/kmem read failed");
else
printf("/dev/kmem read buf = %ld\n", *(unsigned long *)buf);
close(fd);
return n;
}
/* read from physical memory */
int read_mem(off_t offset, void* buf, size_t count)
{
int fd;
int n;
int page_size;
void *map_base;
unsigned long value;
printf("/dev/mem: the offset is %lx\n", offset);
fd = open("/dev/mem", O_RDONLY);
if (fd < 0)
{
perror("open /dev/mem failed");
return -1;
}
if(1){
page_size = getpagesize();
printf("the page size = %d\n", page_size);
map_base = mmap(0,page_size,PROT_READ,MAP_SHARED,fd,offset);
if (map_base == MAP_FAILED){
perror("mmap");
exit(1);
}
value = LONG(map_base);
printf("/dev/mem: the value is %ld\n", value);
buf = (unsigned long *)map_base;
}
if(0){
lseek(fd, offset, SEEK_SET);
n = read(fd, buf, count);
if (n != count)
perror("/dev/mem read failed");
else
printf("/dev/mem read buf = %ld\n", *(unsigned long *)buf);
}
close(fd);
return n;
}
int main(int argc, char **argv)
{
FILE *fp;
char addr_str[11]="0x";
char var[51];
unsigned long addr;
unsigned long jiffies;
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); // format of System.map
if (strcmp(var,"jiffies")==0)
break;
} while(r > 0);
if (r < 0) {
printf("could not find jiffies\n");
exit(-1);
}
addr = strtoul(addr_str,NULL,16); //Convert string to unsigned long integer
printf("found jiffies at (%s) %08lx\n",addr_str,addr);
read_kmem(addr, &jiffies, sizeof(jiffies));
printf("jiffies=%ld (read from virtual memory)\n\n", jiffies);
jiffies = 0; //reinit for checking read_mem() below
read_mem(addr-0xC0000000, &jiffies, sizeof(jiffies));
printf("jiffies=%ld (read from physical memory)\n", jiffies);
return 0;
}
回答by p_l
For the invalid argument in case 1, the problem is offset being non-page aligned. mmap(2)works by manipulating page tables, and such works only on multiplies of page-size for both size and offset
对于情况 1 中的无效参数,问题是非页面对齐的偏移量。mmap(2)通过操作页表来工作,并且这种工作仅适用于大小和偏移量的页面大小的乘数
As for the second case, I'm not sure if you're guaranteed to have kernel space begin at 3G boundary. Also, I'm pretty sure that's the boundary of kernel's virtual space, not location in physical memory - so on beagle board, quite possibly you ended up with a wrapped-around offset pointing who-knows-where.
至于第二种情况,我不确定您是否保证内核空间从 3G 边界开始。另外,我很确定这是内核虚拟空间的边界,而不是物理内存中的位置 - 所以在小猎犬板上,很可能你最终得到了一个环绕的偏移量,指向谁知道在哪里。
I think what you might need is PHYS_OFFSET, notPAGE_OFFSET.
我认为您可能需要的是PHYS_OFFSET,而不是PAGE_OFFSET。
回答by Costa
I've tried combinations or offset and bs for dd and found this solution:
我已经为 dd 尝试了组合或偏移和 bs 并找到了这个解决方案:
On PC, in build directory I've found location of jiffies.
在 PC 上,在构建目录中,我找到了 jiffies 的位置。
grep -w jiffies System.map
c04660c0D jiffies
grep -w jiffies System.map
c04660c0D jiffies
On PandaBoard:
在熊猫板上:
In /proc/iomem you can see:
在 /proc/iomem 中,您可以看到:
80000000-9c7fffff : System RAM
80008000-80435263 : Kernel code
80464000-804d0d97 : Kernel data
a0000000-bfefffff : System RAM
80000000-9c7ffffff:系统 RAM
80008000-80435263:内核代码
80464000-804d0d97:内核数据
a0000000-bfeffffff:系统 RAM
RAM starts from physical 80000000, and Kernel data start on 80464000. Looks similar to address of jiffies.
RAM 从物理 80000000 开始,内核数据从 80464000 开始。看起来类似于 jiffies 的地址。
Then convert from virtual address to phys: virt - 0xC000000 + 0x8000000.
然后从虚拟地址转换为 phys:virt - 0xC000000 + 0x8000000。
dd if=/dev/mem skip=$((0x804660c)) bs=$((0x10)) count=1 2> /dev/null | hexdump
0000000 02b9 00020001 0000 0000 0000 0000 0000
0000010
dd if=/dev/mem skip=$((0x804660c)) bs=$((0x10)) count=1 2> /dev/null | hexdump
0000000 02b9 00020001 0000 0000 0000 0000 0000
0000010
Try several times and see how the value is incrementing.
多试几次,看看值是如何递增的。
Summary: /dev/mem uses phys address, RAM starts at phys address 0x8000000
总结:/dev/mem 使用 phys 地址,RAM 从 phys 地址 0x8000000 开始