【问题标题】:How to emulate memory-mapped I/O如何模拟内存映射 I/O
【发布时间】:2011-12-15 17:46:47
【问题描述】:

我有一些我想模拟的硬件;我想知道我是否可以在这样的低水平上做到这一点。硬件有很多寄存器,我把它们安排在一个结构中:

#include <stdint.h>
struct MyControlStruct
{
    uint32_t data_reg_1;
    uint32_t data_reg_2;
    uint32_t dummy[2]; // to make the following registers have certain addresses
    uint32_t control_reg_1;
    uint32_t control_reg_2;
};
volatile struct MyControlStruct* MyDevice = (struct MyControlStruct*)0xDeadF00;

所以,我想支持以下语法在 Windows 和 linux 上进行硬件访问:

MyDevice->data_reg_1 = 42;
MyDevice->data_reg_2 = 100;
MyDevice->control_reg_1 = 1;

当执行最后一行代码时,我希望硬件模拟器“唤醒”并做一些事情。我可以在 Windows 和/或 linux 上实现这个吗?我想过以某种方式捕获“分段错误”信号,但不确定这是否可以在 Windows 上完成,或者根本就没有。

我看了mmap的手册页;好像有帮助,但我不明白如何使用它。

当然,我可以通过定义WriteToMyDevice 之类的函数来抽象对硬件的访问,一切都会很容易(也许),但我想知道我是否可以以这种确切的方式安排对我的硬件的访问。

【问题讨论】:

  • 您的“低级别”太高了...在 Windows 和 Linux 中,硬件访问都是从内核模式完成的。你需要考虑更多的实现细节——如何与硬件对话。例如,您可以编写真正的驱动程序和驱动程序仿真。
  • 那么,在用户态使用预定义的内存地址是不可能的?
  • 您不能使用直接内存访问从用户模式 ​​Windows/Linux 直接与硬件设备通信。这仅在内核模式下是可能的。这就是为什么我建议您首先考虑实现细节。没有它们,您实际上不知道要效仿什么。
  • 只有一条评论,mmap() 对您的问题没有帮助。它为您的进程分配一个新的线性地址间隔,并根据需要将其与空闲页框链接。我理解您想要做什么的方式,您希望在用户空间中的一个内存位置将值更改为 1 时发送信号,正如 Alex 指出的那样,我不熟悉任何简单的方法。
  • ...在 Linux 中就是这样。对windows不太熟悉。

标签: c hardware-interface memory-mapping


【解决方案1】:

我最初误解了你的问题。你有一块内存映射硬件,你希望你的仿真是二进制兼容的。在 Windows 上,您可以使用 VirtualAlloc 为结构分配内存,并将其设为保护页并使用 SEH 捕获对它的任何访问。

【讨论】:

    【解决方案2】:

    实际上,您的模拟器(相当粗略地)可以在 linux 上使用纯用户空间代码实现。

    要构建模拟器,只需让第二个线程或进程(使用共享内存,或者可能是 mmap'd 文件和 inotify)监视正在模拟内存映射设备的内存

    对于真正的硬件驱动程序,您将需要一点内核代码,但这可能只是将实际硬件地址映射到具有适当权限的用户空间的东西。实际上,这使现代多用户操作环境退化为像旧的 dos 盒或简单的微控制器一样 - 不是很好的做法,但至少在安全性不是问题的情况下是可行的。

    您可以考虑的另一件事是在虚拟机中运行代码。

    如果您将要使用的代码是您自己的,最好以可移植的方式开始编写,将硬件访问抽象为您可以为每个平台(即操作系统、硬件)重新编写的函数版本或物理/模拟)。如果您需要为其创建环境的是其他人的现有代码,这些技术会更有用。您可以考虑的另一件事(如果原始集成不是太紧密)是使用特定函数的动态库级别拦截,例如在 linux 上使用 LD_PRELOAD 或在 Windows 上使用包装 dll。或者就此而言,修补二进制文件。

    【讨论】:

      【解决方案3】:

      原则上,您可以为SIGSEGV 编写(不可移植的)处理程序,该处理程序将捕获和处理对不需要的页面的访问,并可以检查是否访问了指定的地址。

      要在 Linux 下执行此操作,您需要将 sigaction 系统调用与 SA_SIGINFO 一起使用,并使用信号处理程序的第三个参数 ucontext_t*

      这是极其不可移植的:你必须为不同的 Unix 编写不同的代码(甚至你的 Linux 内核的版本号可能很重要)以及在更换处理器时。

      而且我听说 Linux 内核在这种处理上不是很快。

      其他更好的内核(Hurd,Plan9)提供用户级分页,这应该会有所帮助。

      【讨论】:

        猜你喜欢
        • 2016-05-27
        • 2017-06-08
        • 1970-01-01
        • 2011-01-19
        • 2017-05-17
        • 1970-01-01
        • 2016-11-16
        • 2011-10-11
        • 2012-03-25
        相关资源
        最近更新 更多