繁体   English   中英

我是否在这里正确使用 atomic<> over shared memory

[英]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.

我担心的是:

  • 没有新的位置,只是重新解释演员 + 初始值集。
  • 也不是 d-tor。
  • 为什么 sizeof(uint16_t) 与 sizeof(std::atomic<uint16_t>) 相同 - 如果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>操作编译为单个汇编指令:

https://gcc.godbolt.org/z/d6bK9jfza

我担心的是:

  • 没有新的位置,只是重新解释演员 + 初始值集。
  • 也不是 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 上的gccclang 没有在 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM