当前位置: 首页 > 面试题库 >

无法读取共享内存

贺海
2023-03-14
问题内容

我试图在共享内存上发布一些随机的东西;出于某些奇怪的原因,阅读器没有选择发件人写的东西

#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/types.h>
#include <cstdio>

class SHM {
    volatile char* _ptr;
public:
    SHM() {
        const auto handle = shm_open("myTest", O_RDWR|O_CREAT, 0666);
        const auto size =  4 * 1024 * 1024;
        if (-1 == ftruncate(handle, size)) {
            throw;
        }
        _ptr = (volatile char*)mmap(0,size , PROT_READ | PROT_WRITE, MAP_SHARED, handle, 0);

        if(_ptr == MAP_FAILED){
            throw;
        }

                int rc = fchmod(handle, 0666);
                if (rc == -1) {
            throw;
                }
    }

    bool read(uint64_t& magic, uint64_t& time) {
        const uint64_t newVal = *(uint64_t*)_ptr;
        if (newVal != magic) {
            magic = newVal;
            printf("value changed!!!\n");
            time = *(uint64_t*)(_ptr + sizeof(magic));
            return true;
        }
        //printf("old value: %lu\n", newVal);
        return false;
    }

    void publish(const uint64_t time) {
        __sync_fetch_and_add((uint64_t*)_ptr, time);
        __sync_synchronize();
        *(uint64_t*)(_ptr + sizeof(uint64_t)) = time;
    }
};

这是发件人:

#include <ctime>
#include <unistd.h>
#include <cstdlib>
#include <cstdint>
#include "shm.h"

int main() {
    SHM shm;
    timespec t;
    for (auto i = 0; i < 10000; i++) {
        if (0 == clock_gettime(CLOCK_REALTIME, &t)) {
            const uint64_t v = t.tv_sec * 1000 * 1000 * 1000 + t.tv_nsec;
            shm.publish(v);
            printf("published %lu\n", v);
            usleep(100);
        }
    }
}

这是读者:

#include <iostream>
#include "shm.h"

int main() {
    SHM shm;
    uint64_t magic = 0;
    uint64_t t = 0;
    while (true) {
        if (shm.read(magic, t)) {
            printf("%lu, %lu\n", magic, t);
        }
    }
}

如果重新启动阅读器,则阅读器确实能够读取发送方已写入的最后一个值。

但是,如果我先启动阅读器,然后再启动发送器,则阅读器不会拾取发送器写入的所有值。

为了使这个更奇怪,如果我在SHM :: read()中取消对printf语句的注释,那么读者有时可以使用。

任何的想法?

GCC版本:

g++ (GCC) 7.2.1 20170829 (Red Hat 7.2.1-1)

问题答案:

我发现了几个问题,但是,我不确定它们是否可以解决您的问题。

  1. nameshm_open应开始/携带使用。
  2. readpublish演员一定不能丢弃volatile。例如:const uint64_t newVal = *(uint64_t volatile*)_ptr;。更好的是,丢弃volatile并使用std::atomic

尽管涉及不同的过程,但是仍然存在相同的对象被多个执行线程访问的情况,并且这些线程中的至少一个会修改共享对象。

我进行了上述更改。使用std::atomic固定的:

class SHM {
    void* _ptr;
public:
    SHM() {
        const auto handle = shm_open("/myTest", O_RDWR|O_CREAT, 0666);
        const auto size =  4 * 1024 * 1024;
        if (-1 == ftruncate(handle, size))
            throw;

        _ptr = mmap(0,size , PROT_READ | PROT_WRITE, MAP_SHARED, handle, 0);

        if(_ptr == MAP_FAILED)
            throw;
    }

    bool read(uint64_t& magic, uint64_t& time) {
        auto p = static_cast<std::atomic<uint64_t>*>(_ptr);
        const uint64_t newVal = p[0];
        if (newVal != magic) {
            magic = newVal;
            printf("value changed!!!\n");
            time = p[1];
            return true;
        }
        return false;
    }

    void publish(const uint64_t time) {
        auto p = static_cast<std::atomic<uint64_t>*>(_ptr);
        p[0] += time;
        p[1] = time;
    }
};

void sender() {
    SHM shm;
    timespec t;
    for (auto i = 0; i < 10000; i++) {
        if (0 == clock_gettime(CLOCK_REALTIME, &t)) {
            const uint64_t v = t.tv_sec * 1000 * 1000 * 1000 + t.tv_nsec;
            shm.publish(v);
            printf("published %lu\n", v);
            usleep(100);
        }
    }
}

void reader() {
    SHM shm;
    uint64_t magic = 0;
    uint64_t t = 0;
    while (true) {
        if (shm.read(magic, t)) {
            printf("%lu, %lu\n", magic, t);
        }
    }
}

int main(int ac, char**) {
    if(ac > 1)
        reader();
    else
        sender();
}

有了std::atomic您,您可以拥有更多控制权。例如:

struct Data {
    std::atomic<uint64_t> time;
    std::atomic<uint64_t> generation;
};

// ...

    bool read(uint64_t& generation, uint64_t& time) {
        auto data = static_cast<Data*>(_ptr);

        auto new_generation = data->generation.load(std::memory_order_acquire); // 1. Syncronizes with (2).
        if(generation == new_generation)
            return false;

        generation = new_generation;
        time = data->time.load(std::memory_order_relaxed);
        printf("value changed!!!\n");
        return true;
    }

    void publish(const uint64_t time) {
        auto data = static_cast<Data*>(_ptr);

        data->time.store(time, std::memory_order_relaxed);
        data->generation.fetch_add(time, std::memory_order_release);  // 2. (1) Synchronises with this store.
    }


 类似资料:
  • 问题内容: 我正在尝试使用linux名称空间。特别是pid名称空间。 我以为我可以用bash测试一下,但是遇到了这个问题: 从那里运行ls进行了核心转储。退出是唯一的可能。 为什么这样做呢? 问题答案: 该错误是由PID 1进程在新名称空间中退出引起的。 在bash开始运行之后,bash将派生几个新的子流程来执行某些操作。如果在不使用- f的情况下运行取消共享,则bash将具有与当前“取消共享”进

  • 我还在努力把我的头缠在共享的记忆上。我试图完成的是拥有一个豆荚数组。每个pod还将包含一个KeyValue数组。 因此,在这一点上,我在一个pod中有一个keyValue,如果我从同一个文件中读取共享内存,我就没有问题了。 当我试图从另一个进程中读取时,问题就出现了。我有以下文件 printf导致seg错误,我认为它试图访问没有分配任何内容的部分内存,而printf实际上会在我的第一个文件中打印。

  • 共享内存是两个或多个进程共享的内存。 但是,为什么我们需要共享内存或其他通信方式呢? 重申一下,每个进程都有自己的地址空间,如果任何进程想要将自己的地址空间的某些信息与其他进程进行通信,那么只能通过IPC(进程间通信)技术进行。 我们已经知道,通信可以在相关或不相关的进程之间进行。 通常,使用管道或命名管道来执行相互关联的进程通信。 可以使用命名管道或通过共享内存和消息队列的常用IPC技术执行无关

  • EasySwoole对Swoole table进行了基础的封装。 方法列表 getInstance() 该方法用于获取TableManager管理器实例 add($name,array $columns,$size = 1024) 该方法用于创建一个table get($name):?Table 该方法用于获取已经创建好的table 示例代码 TableManager::getInstance()

  • shmat是shared memory attach的缩写。而attach本意是贴的意思。 如果进程要使用一段共享内存,那么一定要将该共享内存与当前进程建立联系。即经该共享内存挂接(或称映射)到当前进程。 shmdt则是shmat的反操作,用于将共享内存和当前进程分离。在共享内存使用完毕后都要调用该函数。 函数原型 #include <sys/types.h> #include <sys/shm.

  • 共享内存的控制 函数原型 #include <sys/ipc.h> #include <sys/shm.h> int shmctl(int shmid, int cmd, struct shmid_ds *buf); 参数 shmid 由shmget函数生成,不同的key值对应不同的id值。 cmd 操作字段,包括: 公共的IPC选项(ipc.h中): IPC_RMID //删除 IPC_SET