我有一个共享内存映射文件,它以某种类型的有效负载开头:
struct header
{
unsigned long long version = 0;
std::atomic<bool> ok = false;
// other fields
};
据我所知,
std::atomic<bool>
是无锁的,因此,根据其他一些 stackoverflow 答案,我可以“转换”指向 header
的指针,并且 rw
可以正常工作,并且我可以应用原子操作它。例如:
_fd = open("/dev/shm/myshfile", O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
void* ptr = mmap(nullptr, 4 * 1024 + _sz,
PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0);
header* hdr = static_cast<header*>(ptr);
if (!hdr->ok) {
// bla bla bla
hdr->ok = true;
}
为了简洁起见,我省略了有关集群的代码(根据我是否要读取或写入文件来获取共享锁或独占锁)以及其他一些各种检查,但总的来说,我必须做一些工作“初始化”或“清理”文件,以防文件为空或出于其他原因,这意味着我无法确定文件是否足够大以映射标头。
所以我正在考虑读取标题而不像这样进行映射:
header hdr = {};
int bytes_read = read(_fd, &hdr, sizeof(hdr)); // (1)
if (bytes_read != sizeof(hdr) || "headers contents are not ok") {
flock(_fd, LOCK_EXCL);
refill_file_from_scratch();
_hdr = /* mmap as before */;
new (_hdr) header();
_hdr->ok = true;
}
现在问题来了,
(1)
的队伍安全吗?我可以“读取”包含原子的 struct
然后安全地使用该原子吗?
更新:关于
std::atomic
是否可以简单复制(不是)的话题已经发生了变化。但是,鉴于此代码:
那又怎样?
int fd = creat("path", S_IRUSR | S_IWUSR);
auto* buf = mmap(nullptr, sizeof(header), PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0);
auto* hdr = new (buf) header;
hdr->ok = true;
unsigned char buf2[sizeof(header)];
memcpy(buf2, buf, sizeof(header));
auto* hdr2 = (header*)buf2;
我正在复制
std::atomic<bool>
类型的对象吗?因为我只是投射缓冲区。对吗?
std::is_trivially_copyable
是告诉您是否可以安全地将一个实例的字节复制到另一个实例的特征。
我尝试过这里:
#include <atomic>
#include <iostream>
#include <type_traits>
struct header
{
unsigned long long version = 0;
std::atomic<bool> ok = false;
// other fields
};
int main(){
std::cout << std::atomic<bool>::is_always_lock_free << "\n";
std::cout << std::is_trivially_copyable_v<std::atomic<bool>> << "\n";
std::cout << std::is_trivially_copyable_v<header> << "\n";
}
接收输出:
1
1
1
来自cppreference:
不存在潜在重叠子对象的普通可复制类型的对象是唯一可以使用
安全复制或使用std::memcpy
/
std::ofstream::write()
与二进制文件序列化的 C++ 对象。
std::ifstream::read()
“潜在重叠”部分不是
std::trivially_understandable
,但幸运的是与这里无关。