C 如何模拟内存映射I/O

C 如何模拟内存映射I/O,c,hardware-interface,memory-mapping,C,Hardware Interface,Memory Mapping,我有一些我想模仿的硬件;我想知道我是否能在这样的低水平上做到这一点。硬件有许多寄存器,我将它们排列在一个结构中: #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_

我有一些我想模仿的硬件;我想知道我是否能在这样的低水平上做到这一点。硬件有许多寄存器,我将它们排列在一个结构中:

#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上实现此功能吗?我考虑过如何捕捉“分段错误”信号,但不确定这是否可以在Windows上完成,或者根本无法完成

我看了mmap的手册页;它似乎能帮上忙,但我不明白如何使用它


当然,我可以通过定义函数来抽象对硬件的访问,比如
WriteToMyDevice
,一切都会很简单(可能),但我想了解我是否可以用这种精确的方式安排对硬件的访问。

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

要在Linux下实现这一点,需要将
sigaction
系统调用与
SA_SIGINFO
一起使用,并使用信号处理程序的
ucontext\u t*
第三个参数

这是非常不可移植的:您必须为不同的Unix编写不同的代码(甚至Linux内核的版本号也可能会有影响)以及在更换处理器时

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


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

事实上,您的模拟器(相当粗糙)可以在linux上使用纯用户空间代码

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

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

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


如果您将要练习的代码是您自己的,那么最好首先以可移植的方式编写,将硬件访问抽象为可以为每个平台(即操作系统、硬件版本或物理/仿真)重新编写的功能。如果您需要为其他人的现有代码创建环境,则这些技术更有用。另一件你可以考虑的事情(如果原件不是太紧密的集成)是使用动态库级拦截特定的函数,例如Linux上的LDHPyLoad或Windows上的包装DLL。或者,修补二进制文件。

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

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