我有以下代码通过共享内存进行进程间通信。一个进程写入日志,另一个进程从中读取。一种方法是使用信号量,但在这里我使用的是 atomic_t 类型的原子标志( log_flag ),它位于共享内存中。日志( log_data )也是共享的。
现在的问题是,这适用于x86架构还是需要信号量或互斥量?如果我使 log_flag 非原子怎么办?鉴于x86具有严格的内存模型和主动缓存一致性,并且优化不适用于指针,我认为它仍然有用吗?
编辑:请注意,我有一个8核的多核处理器,所以我在忙等待时没有任何问题!
// Process 1 calls this function
void write_log( void * data, size_t size )
{
while( *log_flag )
;
memcpy( log_data, data, size );
*log_flag = 1;
}
// Process 2 calls this function
void read_log( void * data, size_t size )
{
while( !( *log_flag ) )
;
memcpy( data, log_data, size );
*log_flag = 0;
}
答案 0 :(得分:4)
您可能希望在循环中使用以下宏,以避免给内存总线带来压力:
#if defined(__x86_64) || defined(__i386)
#define cpu_relax() __asm__("pause":::"memory")
#else
#define cpu_relax() __asm__("":::"memory")
#endif
此外,它充当内存屏障("memory"
param。),因此无需将log_flag
声明为volatile
。
但我觉得这样做太过分了,应该只针对硬实时的东西。你可以使用futex。也许您可以简单地使用管道,它几乎可以用于所有目的。
答案 1 :(得分:2)
我不建议这样做有两个原因:首先,尽管编译器可能无法优化指针访问,但这并不意味着处理器不会缓存指向的值。其次,它是原子的这一事实不会阻止while循环结束与执行* log_flag = 0的行之间的读访问。互斥体更安全,但速度要慢得多。
如果您正在使用pthread,请考虑使用RW互斥锁来保护整个缓冲区,这样您就不需要一个标志来控制它,互斥锁本身就是标志,并且在频繁执行时你会有更好的性能读取。
我也不建议做空的while()循环,你会以这种方式占用所有处理器。在循环中放置一个usleep(1000),让处理器有机会呼吸。
答案 2 :(得分:1)
为什么你应该使用信号量而不是依赖旗帜,有很多原因。
问题2可能看起来很可能很少出现并且追踪问题会很困难。所以请帮自己一个忙,并使用正确的操作系统原语。他们将保证事情按预期工作。
答案 3 :(得分:1)
只要log_flag
是原子的,你就可以了。
如果log_flag
只是一个普通的布尔,你不能保证它会起作用。
编译器可以重新排序指令
*log_flag = 1;
memcpy( log_data, data, size );
只要在log_flag
内没有访问memcpy
,这在单处理器系统上在语义上是相同的。您唯一的优势可能是劣等优化器,无法推断memcpy
中访问的变量。
cpu 可以重新排列说明书 它可以选择在循环之前加载log_flag以优化管道。
缓存可能会重新排序内存写入
包含log_flag
的缓存行可能会在包含data
的缓存行之前同步到另一个处理器。
你需要的是一种告诉编译器,cpu和缓存“脱手”的方法,这样他们就不会对订单做出假设。这只能用内存栅栏来完成。 std::atomic
,std::mutex
和信号量都在其代码中嵌入了正确的内存栅栏指令。