Mmap返回void *,但不是volatile void*
。如果我使用mmap来映射共享内存,那么另一个进程可能正在写入该内存,这意味着来自同一内存位置的两个后续读取可能会产生不同的值 - 这是指volatile的确切情况。那么为什么它不会返回一个易变的空隙*?
我最好的猜测是,如果你有一个专门写入共享内存段的进程,它不需要通过volatile指针查看共享内存,因为它总能正确理解存在的内容;编译器为防止冗余读取所做的任何优化都无关紧要,因为没有其他任何内容可以写入并更改其下的值。还是有其他一些历史原因?我倾向于说返回volatile void*
将是一个更安全的默认值,那些想要进行优化的人可以手动转换为void *。
POSIX mmap说明:http://opengroup.org/onlinepubs/007908775/xsh/mmap.html
答案 0 :(得分:7)
实现共享内存只是mmap()
使用的一小部分。事实上,最常见的用途是创建私有映射,包括匿名和文件支持。这意味着,即使我们接受了关于要求共享内存访问的volatile
限定指针的争论,在一般情况下这样的限定符也是多余的。
请记住,您始终将最终限定符添加到指针类型而不进行强制转换,但您无法删除它们。因此,使用当前的mmap()
声明,您可以执行以下两项操作:
volatile char *foo = mmap(); /* I need volatile */
和此:
char *bar = mmap(); /* But _I_ do not */
根据您的建议, common 案例中的用户必须将易失性广告投放。
答案 1 :(得分:6)
贯穿许多软件系统的深刻假设是,大多数程序员都是顺序程序员。这最近才开始改变。
mmap
有许多与共享内存无关的用法。如果程序员正在编写多线程程序,他们必须采取自己的步骤来确保安全。使用互斥锁保护每个变量不是默认值。同样地,mmap
不假设另一个线程将对同一共享内存段进行有争议的访问,或者甚至是如此映射的段将被另一个线程访问。
我也不相信将mmap
标记为volatile
会对此产生影响。程序员仍然必须确保访问映射区域的安全性,不是吗?
答案 2 :(得分:4)
易失性只会覆盖一个读取(取决于架构可能是32位或其他东西,因此非常有限。通常你需要写超过1个机器字,你无论如何都有引入某种锁定。
即使它是易失性的,你也可以很容易地让2个进程从同一个内存读取不同的值,所需要的只是一个3.在从1.进程读取和读取之间的纳秒内写入内存的过程来自2.进程(除非你能保证2个进程在几乎完全相同的时钟周期内读取相同的内存。
因此 - mmap()尝试处理这些事情是没有用的,最好让程序员如何处理对内存的访问并在需要的地方将指针标记为volatile - 如果内存是共享的 - 你将需要让所有参与者合作并意识到他们如何更新内存相对于彼此的内容 - 超出mmap范围的东西,以及挥发性无法解决的东西。
答案 3 :(得分:3)
我认为volatile
不符合你的想法。
基本上,它只是告诉编译器不要通过将其值存储在寄存器中来优化变量。这会强制它在每次引用时检索值,如果另一个线程(或其他)可以在过渡期间更新它,这是一个好主意。
该函数返回一个void *,但它不会被更新,因此调用volatile是没有意义的。即使您将值赋给本地volatile void *,也不会获得任何结果。
答案 4 :(得分:0)
出于性能原因,它可能就是这样做的,默认情况下不提供额外的内容。如果您知道在您的特定体系结构中,处理器不会重新排序写入/读取,您可能根本不需要易失性(可能与其他同步结合)。编辑:这只是一个例子 - 可能存在各种其他情况,您知道每次访问内存时都不需要强制重读。
如果你需要确保每次访问时都从内存中读取所有地址,const_cast(或C风格的转换)会自动转换为返回值。
答案 5 :(得分:0)
类型volatile void *
或void * volatile
是荒谬的:您不能取消引用void *
,因此为其指定类型限定符没有意义。
而且,既然您无论如何需要转换为char *
或任何数据类型,那么也许这是指定波动率的正确位置。因此,定义好的API可以将标记内存的责任放在脚下/易变的范围内。
那说,从大图POV,我同意你的看法:mmap应该有一个返回类型,说明编译器不应该缓存这个范围。