我对C和C++中volatile
语义的理解是,它将内存访问变成了(可观察到的)副作用。每当读取或写入内存映射文件(或共享内存)时,我都希望指针是volatile限定的,以表明这实际上是I/O。(John Regehr写了一篇关于volatile
语义的很好的文章)。
此外,我认为使用memcpy()
这样的函数访问共享内存是不正确的,因为签名表明volatile限定已被放弃,并且内存访问不被视为I/O。
在我看来,这是一个支持std::copy()
的论点,在这里volatile限定符不会被丢弃,内存访问被正确地视为I/O。
但是,我使用指向易失性对象的指针和std::copy()
访问内存映射文件的经验是,它比仅仅使用memcpy()
要慢好几个数量级。我很想得出结论,也许clang和GCC对volatile
的处理过于保守。是这样吗?
如果我想要遵循标准的文字并使其恢复我所依赖的语义,那么对于访问volatile
共享内存有什么指导呢?
标准[介绍执行]§14中的相关引用:
读取由volatile glvalue指定的对象、修改对象、调用库I/O函数,或者调用执行其中任何操作的函数都是副作用,它们是执行环境状态的变化。表达式(或子表达式)的求值通常包括值计算(包括为glvalue求值确定对象的标识和为prvalue求值提取先前分配给对象的值)和副作用的启动。当返回对库I/O函数的调用或评估通过volatile glvalue的访问时,即使调用(如I/O本身)或volatile访问隐含的一些外部操作可能尚未完成,副作用也被认为是完成的。
我觉得你想多了。我不认为mmap
或等价物(这里我将使用POSIX术语)内存是易失性的。
从编译器的角度来看,mmap
返回一个对象,该对象经过修改,然后被赋予msync
或munmap
或_exit
期间隐含的unmap。这些函数需要被视为I/O,而不是其他。
您可以将mmap
替换为malloc
+read
,将munmap
替换为write
+free
,这样您就可以在何时以及如何完成I/O方面得到大部分保证。
请注意,这甚至不需要将数据反馈到munmap
,用这种方式演示它更容易。您可以让mmap
返回一段内存并将其保存在内部的列表中,然后使用一个函数(我们称之为msyncall
),该函数没有任何参数,可以写出先前返回的所有内存对mmap
的所有调用。然后,我们可以根据这个进行构建,即任何执行I/O的函数都有一个隐式的msyncall
。不过我们没必要走那么远。从编译器的观点来看,libc是一个黑盒,其中某个函数返回了一些内存,在对libc进行任何其他调用之前,这些内存必须是同步的,因为编译器无法知道先前从libc返回的哪些内存位仍然被引用并且在内部处于活动状态。
上面这一段是它在实践中是如何运作的,但是我们如何从一个标准的角度来接近它呢?我们先来看一个类似的问题。对于线程,共享内存只在一些非常特定的函数调用时进行同步。这是非常重要的,因为现代CPU重新排序读写,内存障碍非常昂贵,而且旧的CPU可能需要显式的缓存刷新,然后写入的数据才会被其他人(不管是其他线程、进程还是I/O)看到。mmap
的规范规定:
当将mmap()与任何其他文件访问方法结合使用时,应用程序必须确保正确的同步
但它没有指定同步是如何完成的。我知道在实践中,同步必须是msync
,因为仍然有一些系统的读/写操作没有使用与mmap相同的页面缓存。
C11 6.7.3类型限定符,第7段规定: 这里,指针的类型为,但我关心的是当实际指向的对象是非易失性的时会发生什么,特别是编译器是否可以将从对的单个访问转换为以下形式的两个访问: 这显然会使代码不正确。因此,目标是确定是否所有这样的指向对象实际上都需要。
我正在使用一个API,该API为内存映射的I/O提供指针。它通过填写指针到指针的参数来实现这一点: 因为这是内存映射的I/O,所以我很确定我应该在这里使用。(我不知道为什么他们没有将参数设为。) 但是,我的volatile void**不能隐式转换为函数的,因此我不能直接传入它: 我目前正在使用一个额外的变量和一个单独的赋值步骤来解决这个问题: 这似乎很冗长。在这种情况下,当指针传入时,将vola
读 # mmap_read.py import mmap with open('lorem.txt', 'r') as f: with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m: print('First 10 bytes via read :', m.read(10
我正在使用函数迭代
假设有一个结构 我得到一个指向该结构的成员的指针,比如作为某个函数的参数: 如何获取指向包含对象的指针?最重要的是:在不违反标准中的某些规则的情况下,也就是说,我想要标准定义的行为,而不是未定义或实现定义的行为。 附带说明:我知道这规避了类型安全。