You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ubuntu交叉编译树莓派Zero2 W项目中,如何初始化共享内存内的std::recursive_mutex及设备数据结构

Ubuntu交叉编译树莓派Zero2 W项目中,如何初始化共享内存内的std::recursive_mutex及设备数据结构

这个问题我之前做跨进程共享内存场景时也碰到过!核心原因是std::recursive_mutex是不可拷贝、不可赋值的类型,你直接写device_data_->lock_ = {}属于赋值操作,而它的赋值运算符已经被标准库显式删除了,所以编译器会抛出你看到的错误。要解决这个问题,你不能先把共享内存mmap成结构体指针再逐个赋值成员,而是要在共享内存的原始地址上原地构造整个Lps22DeviceData对象,这样才能正确初始化所有非POD成员(包括互斥锁、chrono时间点这类需要构造函数的类型)。

具体解决方案:用Placement New原地构造对象

你的代码需要做以下调整:

  1. 先完成shm_open和ftruncate的步骤,保持不变;
  2. mmap获取共享内存的原始地址(先存为void*);
  3. 用placement new在这个地址上直接构造Lps22DeviceData实例,这样会自动调用所有成员的构造函数,包括std::recursive_mutex的默认构造;
  4. 之后再初始化其他数据成员。

修改后的代码示例:

int shmem_fd = shm_open(shmem_fname.c_str(), O_RDWR|O_CREAT|O_EXCL, 0666);
if (shmem_fd != -1) {
    // 调整共享内存大小
    int result = ftruncate(shmem_fd, sizeof(Lps22DeviceData));
    if (result == -1) {
        // 处理ftruncate错误:比如close fd、shm_unlink
        close(shmem_fd);
        shm_unlink(shmem_fname.c_str());
        // 这里可以加错误日志或抛出异常
        return;
    }

    // 先mmap获取共享内存的原始地址
    void* raw_shmem = mmap(NULL, sizeof(Lps22DeviceData), PROT_READ | PROT_WRITE, MAP_SHARED, shmem_fd, 0);
    if (raw_shmem == MAP_FAILED) {
        close(shmem_fd);
        shm_unlink(shmem_fname.c_str());
        // 处理mmap错误
        return;
    }

    // 用placement new在共享内存地址上原地构造Lps22DeviceData对象
    device_data_ = new (raw_shmem) Lps22DeviceData();

    // 现在可以安全地初始化其他数据成员了
    device_data_->read_total_ = 0;
    device_data_->initialized = false;
    device_data_->temperature_measurement_ = 0;
    device_data_->temperature_measurement_system_time_ = system_clock::now();
    device_data_->temperature_measurement_steady_time_ = steady_clock::now();
    device_data_->temperature_response_time = milliseconds(0);

    device_data_->pressure_measurement_ = 0;
    device_data_->pressure_measurement_system_time_ = system_clock::now();
    device_data_->pressure_measurement_steady_time_ = steady_clock::now();
    device_data_->pressure_response_time = milliseconds(0);
    device_data_->initialized = true;
}

关键细节解释

  • 为什么Placement New能解决问题?
    Placement New的作用是在你指定的内存地址上直接调用对象的构造函数,而不是分配新内存。对于Lps22DeviceData里的std::recursive_mutex,它会调用默认构造函数初始化一个可用的互斥锁——在Linux平台上,std::recursive_mutex底层是基于pthread_mutex_t实现的,只要在共享内存里正确初始化,跨进程是可以正常使用的。

  • 其他进程如何访问?
    其他进程打开这个共享内存时,不能再调用Placement New(否则会重复构造,破坏已经初始化的对象),只需要mmap获取地址,然后直接通过Lps22DeviceData*指针访问即可,通过检查initialized标志判断是否已经完成初始化。

  • 额外注意事项

    1. 确保所有进程使用相同版本的C++标准库:因为不同版本的std::recursive_mutex或std::chrono实现可能有内存布局差异,跨进程使用会导致异常。
    2. 最后一个退出的进程需要手动析构对象:当确认没有其他进程再使用共享内存时,调用device_data_->~Lps22DeviceData()析构对象,之后再执行munmap、close、shm_unlink操作。
    3. 结构体布局一致性:编译所有进程时,要保证编译器的对齐选项、C++标准等完全一致,避免结构体内存布局不匹配导致的访问错误。

可选优化:给类添加显式构造函数

你可以给Lps22DeviceData添加一个默认构造函数,把成员初始化逻辑都放在里面,这样代码更整洁,也避免手动赋值时的遗漏:

#include <chrono>
using namespace std::chrono;

class Lps22DeviceData {
public:
    std::recursive_mutex lock_;
    uint64_t read_total_;
    bool initialized;
    int16_t temperature_measurement_;
    time_point<system_clock> temperature_measurement_system_time_;
    time_point<steady_clock> temperature_measurement_steady_time_;
    milliseconds temperature_response_time;

    int32_t pressure_measurement_;
    time_point<system_clock> pressure_measurement_system_time_;
    time_point<steady_clock> pressure_measurement_steady_time_;
    milliseconds pressure_response_time;

    // 显式默认构造函数,统一初始化所有成员
    Lps22DeviceData()
        : lock_(),
          read_total_(0),
          initialized(false),
          temperature_measurement_(0),
          temperature_measurement_system_time_(system_clock::now()),
          temperature_measurement_steady_time_(steady_clock::now()),
          temperature_response_time(milliseconds(0)),
          pressure_measurement_(0),
          pressure_measurement_system_time_(system_clock::now()),
          pressure_measurement_steady_time_(steady_clock::now()),
          pressure_response_time(milliseconds(0))
    {}
};

这样用Placement New构造时,所有成员都会自动完成初始化,你甚至可以省略后面的手动赋值步骤,直接把initialized设为true即可。

备注:内容来源于stack exchange,提问作者chriskot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 10:27:58