在 Linux 中使用 mmap 访问物理地址 space:将正确的参数传递给 mmap

Accessing physical address space using mmap in Linux: passing the correct arguments to mmap

我需要访问和写入我 RAM 中的一些物理地址。我在看这个answer and the definition of mmap

If addr is NULL, then the kernel chooses the (page-aligned) address at which to create the mapping; this is the most portable method of creating a new mapping. If addr is not NULL, then the kernel takes it as a hint about where to place the mapping; on Linux, the kernel will pick a nearby page boundary (but always above or equal to the value specified by /proc/sys/vm/mmap_min_addr) and attempt to create the mapping there.

#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], NULL, 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, page_offset + len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, page_base);

mmap 不接受物理地址——所有这些地址都是虚拟的。第一个参数是一个提示,告诉 虚拟地址 映射应该放在哪里。

您不需要虚拟地址和物理地址相同就可以在某个物理地址写入。相反,您只需要将物理地址转换为虚拟地址。但是,您需要做的是使用 MAP_SHARED,这样您对内存所做的任何更改都会传送到 /dev/mem,并且您不会拥有私人副本。


如果你真的想做一个身份映射,那么有一个(或两个)特殊标志到mmap 必须使用 才能真正起作用,你应该 | 使用 MAP_PRIVATEMAP_SHARED.

MAP_FIXED

Don't interpret addr as a hint: place the mapping at exactly that address. addr must be suitably aligned: for most architectures a multiple of the page size is sufficient; however, some architectures may impose additional restrictions. If the memory region specified by addr and len overlaps pages of any existing mapping(s), then the overlapped part of the existing mapping(s) will be discarded. If the specified address cannot be used, mmap() will fail.

Software that aspires to be portable should use the MAP_FIXED flag with care, keeping in mind that the exact layout of a process's memory mappings is allowed to change significantly between kernel versions, C library versions, and operating system releases. Carefully read the discussion of this flag in NOTES!

这就是 ld.so 用于在预定义地址加载程序的示例

有一个更安全的标志版本:MAP_FIXED_NOREPLACE在后来的Linux内核中,如果它存在,它不会驱逐以前的映射,但是returns一个错误。