在C代码中,我在共享内存中有一个64位值,由连接到该共享内存的不同进程访问。目前读取和写入由自旋锁覆盖,但是在编译中,保证对齐的64位读取不会被“撕裂”(即,读取是原子的)我可以省略自旋锁覆盖,因为“略微陈旧”的版本价值很好。代码需要能够在各种硬件(包括32位和64位计算机; Intel,AMD,Sparc Solaris,IBM Power7和Power8以及许多其他计算机),操作系统环境(包括Linux,Windows和HP-UX)和编译器(gcc和clang,其中许多 - C89或更高版本);虽然如果我“喋喋不休”将螺旋锁保持在无法确定省略它们的安全性的环境中,那就没关系了。当然,更安全的平台,我可以确定它是安全的,更好。
确定这种对齐的64位读取是否保证来自单个64位写入的最安全,最便携的方法是什么?
根据目前为止的评论,最好显示一些相关代码。
在我的机器上,src/include/pg_config.h
:
/* src/include/pg_config.h. Generated from pg_config.h.in by configure. */
/* src/include/pg_config.h.in. Generated from configure.in by autoheader. */
[...]
/* Define to 1 if `long int' works and is 64 bits. */
#define HAVE_LONG_INT_64 1
许多这样的编译时常量是根据./configure
运行时的内容定义的。
在src/include/c.h
:
/*
* 64-bit integers
*/
#ifdef HAVE_LONG_INT_64
/* Plain "long int" fits, use it */
#ifndef HAVE_INT64
typedef long int int64;
#endif
#ifndef HAVE_UINT64
typedef unsigned long int uint64;
#endif
#elif defined(HAVE_LONG_LONG_INT_64)
/* We have working support for "long long int", use that */
#ifndef HAVE_INT64
typedef long long int int64;
#endif
#ifndef HAVE_UINT64
typedef unsigned long long int uint64;
#endif
#else
/* neither HAVE_LONG_INT_64 nor HAVE_LONG_LONG_INT_64 */
#error must have a working 64-bit integer datatype
#endif
我正在努力改进的代码如下。 oldSnapshotControl
是指向共享内存中结构的指针,threshold_timestamp
是该结构中的一个字段,定义为int64
。请注意函数注释中的XXX
块。
/*
* Get timestamp through which vacuum may have processed based on last stored
* value for threshold_timestamp.
*
* XXX: If we can trust a read of an int64 value to be atomic, we can skip the
* spinlock here.
*/
int64
GetOldSnapshotThresholdTimestamp(void)
{
int64 threshold_timestamp;
SpinLockAcquire(&oldSnapshotControl->mutex_threshold);
threshold_timestamp = oldSnapshotControl->threshold_timestamp;
SpinLockRelease(&oldSnapshotControl->mutex_threshold);
return threshold_timestamp;
}
答案 0 :(得分:0)
在C11之前,没有任何数据类型的读取,但可能sig_atomic_t
被认为是原子的(在你解决它的意义上),它根本不是语言的一部分,而且该版本的C可以从来不保证。并且有很多平台,实际上它不会是原子的。
从C11开始,你就拥有语言中包含的原子(广义上),更新版本的gcc和clang支持该功能。如果您想要可移植代码,这将是未来的证明。
如果您对可移植性较差感到满意,可以使用以__sync_
开头的gcc内置扩展,它基本上对基本整数类型执行原子操作。在这里,您将__GCC_HAVE_SYNC_COMPARE_AND_SWAP_XX
的宏XX
替换为字节数的宽度,以告诉您是否支持此类操作。
这些gcc扩展已存在很长时间了,clang也有它们。
答案 1 :(得分:0)
您可以使用sig_atomic_t
是对象的(可能是volatile限定的)整数类型 可以作为原子实体访问,即使存在 异步中断。
从C89开始发现。在<stdint.h>
标头中,定义了以下宏
SIG_ATOMIC_MIN
SIG_ATOMIC_MAX
但是,仅来自C99(您的编译器可能具有类似的实现定义的宏)。然后只需#if SIG_ATOMIC_MAX == 64BIT_MAX
;通过使用实现定义的宏64BIT_MAX
或两者,可以使用limits.h
- s和#if
- s来找到#define
。
虽然您保证sig_atomic_t
将是原子的,但它可能小于64位:例如,我的64位平台sig_atomic_t
仍为int
。
最简单的解决方案可能是依赖外部工具(如cmake,autotools等),并让他们为您找到答案。