【问题标题】:Using mmap with /dev/mem - Is it okay to use reinterpret_cast将 mmap 与 /dev/mem 一起使用 - 可以使用 reinterpret_cast
【发布时间】:2021-10-25 16:09:30
【问题描述】:

我正在使用 mmap/dev/mem。我在 C 中看到 examples 使用以下模式:

#define OFFSET = ...;
int fd = 0;
void* base;

fd = open("/dev/mem", ...);

base = mmap(..., fd, ...);

// Below is line of interest.
*((uint32_t*)(base + OFFSET)) = 23;

第一个问题 - 这里发生了什么?

看起来我们正在向void* 添加一个偏移值,然后将其转换为uint32_t*,然后为其分配一个数字。为什么我们不能将base 声明为uint32_t*?为什么要在分配它之前施放它?

第二个问题 - 我如何在 C++ 中做到这一点?

以下作品来自网络上的点点滴滴。但这基本上是我尝试用reinterpret_caststatic_cast 打地鼠,看看哪一个给了我正确的结果并且不会抛出错误或警告。还将void* 替换为uint8_t*,以防止编译器警告我void 指针上的算术。我不知道它为什么起作用,或者它是否是正确的方法。帮我别开枪打自己的脚?

#define OFFSET = ...;
int fd = 0;
uint8_t* base;

fd = open("/dev/mem", ...);

base = reinterpret_cast<uint8_t*>(mmap(..., fd, ...));

*(reinterpret_cast<uint32_t*>(base + OFFSET)) = 23;

【问题讨论】:

    标签: c++ c linux


    【解决方案1】:

    base + OFFSETgcc 的扩展,其中指针运算适用于void *。 “为什么我们不能将 base 声明为 uint32_t*?”因为算术会出错。 void * 算术以字节为单位。

    对于以字节为单位的算术含义似乎有些混淆。 OFFSET 是值 23 需要去的基数的字节数(不是 32 位整数);所以首先投射到uint32_t 会移动四倍。这种代码是直接写入异构数据结构而不是使用结构的代码的典型。声明一个结构与直接写出访问有优缺点。在古代,他们通常声明一个结构,但由于对齐问题,钟摆已经摆回。更容易确保编译器不会通过以手写方式插入填充来插入结构定义。

    base = reinterpret_cast<uint8_t*>(mmap(..., fd, ...));
    *(reinterpret_cast<uint32_t*>(base + OFFSET)) = 23;
    

    这确实是您在标准 C++ 中必须要做的;尽管对于这样的代码,人们倾向于在 C++ 中使用 C 风格转换(不打算讨论它;只是指出来)。

    【讨论】:

    • 对不起,你能解释更多关于 gcc 扩展的信息或分享一个链接让我阅读更多吗?它不包含在g++ 中?另外,当您说void* 算术以字节为单位时,您是什么意思?在给定的拱门中不是所有指针的大小都相同吗?
    • 找到linkvoid* 算术。那么有效地我们只需要一个指向字节大小的对象的指针吗?
    • 啊我想我现在明白了——所以我们基本上是一次滚动一个字节的内存位置。但是当我们到达我们关心的内存位置时,我们想要写入 4 个字节,因此我们将其转换为 uint32_t?这比一次写一个字节要容易,因为编译器会为我们处理字节序?
    • 注解来自void*可以使用static_cast完成
    • @Mgetz - 你是对的,但看起来从uint8_t* 转换为uint32_t* 仅适用于reinterpret_cast
    猜你喜欢
    • 2018-07-14
    • 2011-09-24
    • 2013-08-21
    • 1970-01-01
    • 1970-01-01
    • 2016-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多