【问题标题】:Accessing physical address space using mmap in Linux: passing the correct arguments to mmap在 Linux 中使用 mmap 访问物理地址空间:将正确的参数传递给 mmap
【发布时间】:2020-10-12 15:27:58
【问题描述】:

我需要访问和写入 RAM 中的一些物理地址。我在看这个answermmap 的定义。

如果 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], 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);

【问题讨论】:

    标签: c linux mmap


    【解决方案1】:

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

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


    如果您真的想要进行身份映射,那么mmap 有一个(或两个)特殊标志,您必须使用 要让它真正起作用,你应该 |MAP_PRIVATEMAP_SHARED

    MAP_FIXED

    不要将 addr 解释为提示:将映射准确地放在该地址。 addr 必须适当对齐:对于大多数体系结构,页面大小的倍数就足够了;但是,某些架构可能会施加额外的限制。如果addr指定的内存区域和 len 与任何现有映射的页面重叠,则现有映射的重叠部分将被丢弃。如果指定的地址不能使用,mmap()会失败。

    渴望可移植的软件应谨慎使用MAP_FIXED 标志,记住进程内存映射的确切布局允许在内核版本、C 库版本和操作系统版本之间发生显着变化。请仔细阅读 NOTES 中有关此标志的讨论!

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

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

    【讨论】:

    • 进一步强调:OP不需要恒等映射,也没有理由相信它甚至可以进行恒等映射。该虚拟地址范围可能已经在使用中,或者可能由于某种原因被保留或不可映射。唯一合法使用MAP_FIXED 是替换您已经创建的现有映射,而不是因为您认为您正在映射的新东西“应该是”在那个地址。
    • @R..GitHubSTOPHELPINGICE 哈哈关于 MAP_PRIVATE 的好点,已修复。好吧,MAP_FIXED 如果你正在制作一个动态链接器,将 exes 加载到静态地址中,可能应该使用它。
    • MAP_FIXED 即使这样也不合适。如果无法进行映射,您需要一个可报告的错误,而不是映射随机的东西,然后执行失控的错误代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-16
    • 2012-08-15
    • 1970-01-01
    • 1970-01-01
    • 2014-04-27
    相关资源
    最近更新 更多