[英]Do I use atomic<> over shared memory correctly here
假設我們有兩個進程。
兩者都使用相同的 mmap-ed memory 區域來傳遞一些信息,在本例中為uint16_t
。
我閱讀了各種意見,如果std::atomic<>
使用is_always_lock_free
,它應該起作用。
所以我做了這個代碼。 代碼按預期工作,但此代碼線程/進程間安全嗎:
#include <cstdint>
#include <atomic>
#include <iostream>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h> // sleep
#include <time.h>
using I = uint16_t;
using AI = std::atomic<I>;
static_assert(std::atomic<I>::is_always_lock_free);
constexpr size_t SIZE = sizeof(AI);
constexpr const char *NAME = "nmmm";
constexpr I STOP = 12345;
void error(const char *msg){
std::cout << msg << '\n';
exit(10);
}
int main(int argc, const char **argv){
std::cout << sizeof(I) << ' ' << sizeof(AI) << '\n';
int fd = shm_open(NAME, O_RDWR | O_CREAT, 0644);
if (fd < 0)
error("shm_open");
int t = ftruncate(fd, SIZE);
if (t < 0)
error("ftruncate");
void *vmem = mmap(nullptr, SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
if (vmem == MAP_FAILED)
error("mmap");
std::cout << "All set up!" << ' ' << vmem << '\n';
AI *ai = reinterpret_cast<AI *>(vmem);
if (argc > 1){
switch(argv[1][0]){
case 'g':
case 'G':
ai->store(0, std::memory_order_relaxed);
while(true){
auto x = ai->load(std::memory_order_relaxed);
std::cout << x << '\n';
if (x == STOP)
break;
sleep(1);
}
case 's':
case 'S':
ai->store(STOP, std::memory_order_relaxed);
break;
default:
{
srand(time(nullptr));
I const x = rand() & 0xFFFF;
std::cout << "set val to " << x << '\n';
ai->store(x , std::memory_order_relaxed);
}
break;
}
}
munmap(vmem, SIZE);
// shm_unlink(NAME);
}
請原諒我像使用rand
一樣混合 C 和 C++ 。
你想做什么——
# console 1
./a.out g # start "Getter"
# console 2
./a.out x # set random value
# or
./a.out s # set "Stop" value, so process in console 1 stops.
我擔心的是:
is_always_lock_free
總是這樣嗎? std::atomic
只是一個花哨的匯編指令嗎?更新
看來, std::atomic<uint16_t>
是 POD。 所以鑄造不是UB(未定義的行為)
static_assert(std::is_pod_v<std::atomic<uint16_t> >);
似乎std::atomic<uint16_t>
操作編譯為單個匯編指令:
我擔心的是:
- 沒有新的位置,只是重新解釋演員 + 初始值集。
- 也不是 d-tor。
- 為什么 sizeof(uint16_t) 與 sizeof(std::atomic<uint16_t>) 相同 - 如果 is_always_lock_free 總是這樣嗎? std::atomic 只是一個花哨的匯編指令嗎?
這些假設可能是正確的,但從形式上講,這些假設導致了 UB。
為避免遇到 UB,請使用 C++20 atomic_ref
。 共享普通的uint16_t
並在進程中使用atomic_ref
。
使用atomic_ref
仍然不能正式保證以進程間方式工作(標准 C++ 中根本沒有進程間),但您不會遇到提到的 UB,因此無需擔心生命周期或不正確的別名。
請注意,您必須保留is_always_lock_free
斷言。 沒有它, atomic_ref
將使用鎖定機制來提供原子語義,這種機制很可能是程序/進程私有的。
因為 C++ atomic
使用起來不是很“清楚”,所以可以使用stdatomic.h
。
https://en.cppreference.com/w/c/atomic/atomic_load
但是我未能在 gcc 上用這些編譯示例。
這就是為什么我決定使用 gcc 內置原子:
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
這些適用於 Linux 上的gcc
和clang
。 沒有在 MacOS 上檢查是的。
這是代碼,與我之前發布的代碼幾乎相似。 再次原諒 C 樣式代碼。
為了使代碼類似於stdatomic.h
提供的代碼,我做了兩個調用內置函數的函數:
template<typename T>
void atomic_store(T *a, T val){
return __atomic_store_n(a, val, __ATOMIC_RELAXED);
}
template<typename T>
void atomic_store(T &a, T val){
return atomic_store(& a, val);
}
template<typename T>
T atomic_load(T *a){
return __atomic_load_n(a, __ATOMIC_RELAXED);
}
template<typename T>
T atomic_load(T &a){
return atomic_load(& a);
}
這些假設適用於所有整數類型,例如 int、short、char、unsigned 等。
如果您決定“挖掘”未來,這是完整的代碼。
#include <cstdio>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h> // sleep
#include <cstdlib> // exit
#include <time.h>
using MyAtomicInt = int;
constexpr size_t SIZE = sizeof(MyAtomicInt);
constexpr const char *NAME = "nmmm";
constexpr MyAtomicInt STOP = 12345;
void error(const char *msg){
printf("%s\n", msg);
exit(10);
}
template<typename T>
void atomic_store(T *a, T val){
return __atomic_store_n(a, val, __ATOMIC_RELAXED);
}
template<typename T>
void atomic_store(T &a, T val){
return atomic_store(& a, val);
}
template<typename T>
T atomic_load(T *a){
return __atomic_load_n(a, __ATOMIC_RELAXED);
}
template<typename T>
T atomic_load(T &a){
return atomic_load(& a);
}
int main(int argc, const char **argv){
int fd = shm_open(NAME, O_RDWR | O_CREAT, 0644);
if (fd < 0)
error("shm_open");
int t = ftruncate(fd, SIZE);
if (t < 0)
error("ftruncate");
void *vmem = mmap(nullptr, SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
if (vmem == MAP_FAILED)
error("mmap");
printf("All set up! %p\n", vmem);
MyAtomicInt *ai = reinterpret_cast<MyAtomicInt *>(vmem);
if (argc > 1){
switch(argv[1][0]){
case 'g':
case 'G':
atomic_store(ai, 0);
while(true){
auto x = atomic_load(ai);
printf("%d\n", x);
if (x == STOP)
break;
sleep(1);
}
case 's':
case 'S':
atomic_store(ai, STOP);
break;
default:
{
srand(time(nullptr));
MyAtomicInt const x = rand() & 0xFFFF;
printf("set val to %d\n", x);
atomic_store(ai, x);
}
break;
}
}
munmap(vmem, SIZE);
// shm_unlink(NAME);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.