问题描述
我需要访问和写入RAM中的某些物理地址。我当时在看这个answer和mmap的定义。
如果addr为NULL,则内核选择(页面对齐的)地址 创建映射的位置;这是最便携的方法 创建一个新的映射。如果addr不为NULL,则内核采用 它是关于放置映射位置的提示;在Linux上,内核 会选择附近的页面边界(但始终高于或等于 / proc / sys / vm / mmap_min_addr指定的值)并尝试创建 那里的映射。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
int main(int argc,char *argv[]) {
if (argc < 3) {
printf("Usage: %s <phys_addr> <offset>\n",argv[0]);
return 0;
}
off_t offset = strtoul(argv[1],NULL,0);
size_t len = strtoul(argv[2],0);
// Truncate offset to a multiple of the page size,or mmap will fail.
size_t pagesize = sysconf(_SC_PAGE_SIZE);
off_t page_base = (offset / pagesize) * pagesize;
off_t page_offset = offset - page_base;
int fd = open("/dev/mem",O_SYNC);
unsigned char *mem = mmap(NULL,page_offset + len,PROT_READ | PROT_WRITE,MAP_PRIVATE,fd,page_base);
if (mem == MAP_FAILED) {
perror("Can't map memory");
return -1;
}
size_t i;
for (i = 0; i < len; ++i)
printf("%02x ",(int)mem[page_offset + i]);
return 0;
}
为什么mmap函数NULL
的第一个参数是?不应该是page_base
吗?我们希望映射从页面基础开始,一直扩展到offset
。
我必须做类似的事情,必须从相同的位置完全开始将值的数组复制到RAM中。这不应该是mmap的调用吗?
unsigned char *mem = mmap(page_base,MAP_SHARED,page_base);
解决方法
mmap
不使用物理地址-所有这些地址都是虚拟的。第一个参数是一个提示,告诉虚拟地址应该在哪里放置映射。
您不需要虚拟地址和物理地址相同就可以在某个物理地址上进行写入。相反,您只需要将物理地址转换为虚拟地址即可。但是,您需要做的是使用MAP_SHARED
,以便将您对内存所做的任何更改都传送到/dev/mem
,并且您将没有私有副本。
如果您确实想要进行身份映射,那么您必须mmap
有一个(或两个)特殊标志使用才能真正起作用,您应该|
或MAP_PRIVATE
MAP_SHARED
来使用。
MAP_FIXED
不要将addr解释为提示:将映射恰好放在该地址。 addr必须适当对齐:对于大多数体系结构,页面大小的倍数就足够了;但是,某些体系结构可能会施加其他限制。如果
addr
和len
与任何现有映射的页面重叠,那么现有映射的重叠部分将被丢弃。如果无法使用指定的地址,则mmap()
将失败。希望可移植的软件应谨慎使用
MAP_FIXED
标志,请记住,允许进程内存映射的确切布局在内核版本,C库版本和操作系统版本之间有很大变化。 。仔细阅读NOTES中有关此标志的讨论!
例如,这是ld.so
用于将程序加载到预定义地址的
在以后的Linux内核中,存在一个更安全的标记版本:MAP_FIXED_NOREPLACE
,该标记不会驱逐先前的映射(如果存在),但是会返回错误。