c++ - 内存映射文件和指向易失对象的指针

标签 c++ c language-lawyer volatile mmap

我对 C 和 C++ 中 volatile 语义的理解是它将内存访问变成了 (observable) side effects .每当读取或写入内存映射文件(或共享内存)时,我都希望指针是 volatile 限定的,以表明这实际上是 I/O。 (John Regehr 在 volatile 的语义上写了一篇非常好的 article)。

此外,我希望使用像 memcpy() 这样的函数来访问共享内存是不正确的,因为签名表明 volatile 限定被丢弃,并且内存访问不被视为我/哦。

在我看来,这是一个支持 std::copy() 的论点,其中 volatile 限定符不会被丢弃,内存访问被正确地视为 I/O。

但是,我使用指向 volatile 对象的指针和 std::copy() 访问内存映射文件的经验是,它比仅使用 memcpy()。我很想得出结论,也许 clang 和 GCC 在处理 volatile 时过于保守。是这样吗?

如果我想遵循标准的字面意思并让它恢复我所依赖的语义,关于访问 volatile 的共享内存有什么指导?


相关引用自标准[intro.execution] §14 :

Reading an object designated by a volatile glvalue, modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment. Evaluation of an expression (or a subexpression) in general includes both value computations (including determining the identity of an object for glvalue evaluation and fetching a value previously assigned to an object for prvalue evaluation) and initiation of side effects. When a call to a library I/O function returns or an access through a volatile glvalue is evaluated the side effect is considered complete, even though some external actions implied by the call (such as the I/O itself) or by the volatile access may not have completed yet.

最佳答案

My understanding of the semantics of volatile in C and C++ is that it turns memory access into I/O

不,它不会那样做。 volatile 所做的只是从程序员向编译器传达某个内存区域可以随时被“其他东西”更改的信息。

“其他”可能是很多不同的东西。示例:

  • 内存映射硬件寄存器
  • 与 ISR 共享的变量
  • 从回调函数更新的变量
  • 与另一个线程或进程共享的变量
  • 通过DMA更新内存区域

由于标准 (5.1.2.3) 保证对 volatile 对象的访问(读/写)可能不会被优化掉,volatile 也可用于阻止某些编译器优化,这是主要用于与硬件相关的编程。

Whenever reading or writing to a memory mapped file (or shared memory) I would expect the the pointer to be volatile qualified

不一定,没有。数据的性质并不重要,重要的是数据的更新方式。

I would expect using functions like memcpy() to access shared memory to be incorrect

总体而言,这取决于您对“共享内存”的定义。这是你整个问题的问题,因为你一直在谈论“共享内存”,这不是一个正式的、定义明确的术语。与另一个 ISR/线程/进程共享内存?

是的,与另一个 ISR/线程/进程共享的内存可能必须声明为volatile,具体取决于编译器。但这只是,因为volatile 可以防止编译器做出不正确的假设并优化以错误方式访问此类“共享”变量的代码。在旧的嵌入式系统编译器上特别容易发生的事情。在现代托管系统编译器上应该没有必要。

volatile 不会导致内存屏障行为。它不会(必然)强制表达式以特定顺序执行。

volatile 当然不保证任何形式的原子性。这就是 _Atomic 类型限定符被添加到 C 语言的原因。

所以回到复制问题 - 如果内存区域在多个 ISR/线程/进程之间“共享”,那么 volatile 根本无济于事。相反,您需要一些同步方式,例如互斥量、信号量或临界区。

In my mind, this is an argument in favor of std::copy(), where the volatile qualifier won't be cast away, and memory accesses being correctly treated as I/O.

不,由于已经提到的原因,这是错误的。

What guidance is there for accessing shared memory with regards to volatile, if I want to follow the letter of the standard and have it back the semantics I rely on?

使用系统特定的 API:s 通过互斥锁/信号量/临界区来保护内存访问。

关于c++ - 内存映射文件和指向易失对象的指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45753903/

相关文章:

c++ - 相关名称的参数相关查找

c++ - 将元素从 char vector 复制到字符串 vector

c++ - mkdir 和 ofstream 的问题

c - 在 C 中使用 atoi() 解析具有二进制值的字符数组

c++ - 是否对未指向序列未定义行为中的元素的指针递增/递减或添加整数值?

c++ - 具有返回抽象类型的函数头是否合法?

c++ - 错误: "Undefined reference to ' main'"

c++ - boost asio在两个线程c++之间进行通信

对 C 限制限定符感到困惑

c - C 中的管道通信