共享内存的pub与sub

文章介绍了一个使用shm_mgr.h实现的共享内存管理器,用于pub/sub通信。在多sub端场景下,当一个sub端被Ctrl+C中断后,导致写入和重复启动pub出现问题。主要问题是文件映射需要删除以及不能正常重启。代码中包含了ShmData、ShmQueue和ShmSlice等结构,以及ShmManager类,用于发布和订阅数据。同时,文章提到了信号处理函数SigHandle来捕获中断信号。
摘要由CSDN通过智能技术生成

shm_mgr.h 公共部分,

但是有缺现,缺是当有多个sub端的时候,ctrl+c打断一个sub端,就无法写入了,

同时mmap映射的文件也需要删除,就是无法很好的重复启动pub,每次都需要删除topic对应的文件。期待大神给留言,如何解决这些问题。

shm_mgr.h

​
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

#include <vector>
#include <iostream>
#include <memory>
#include <functional>
#include <thread>
#include <atomic>

struct ShmData
{
    bool written_;
    long timestamp_;
    size_t size_;
    char data_[1];

    ShmData() : written_(false) {}

    void Write(const char *data, const size_t len)
    {
        written_ = false;
        memcpy(data_, data, len);
        size_ = len;
        timestamp_ = GetTimestamp();
        written_ = true;
    }
    bool Read(std::vector<char> *data, long *time = nullptr)
    {
        if (!written_)
        {
            return false;
        }
        if (time)
        {
            *time = timestamp_;
        }
        data->resize(size_);
        memcpy(data->data(), data_, size_);
        return true;
    }

    static long GetTimestamp()
    {
        struct timespec ts;
        clock_gettime(CLOCK_REALTIME, &ts);
        return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
    }
};

struct ShmQueue
{
    size_t size_;
    int count_;
    int head_;
    char data_[1];

    ShmQueue(const size_t size, const int count)
        : size_(sizeof(ShmData) + size), count_(count), head_(0)
    {
        new (data_) ShmData();
    }

    void Write(const char *data, const size_t len)
    {
        const int next = (head_ + 1) % count_;
        (reinterpret_cast<ShmData *>(data_ + next * size_))->Write(data, len);
        head_ = next;
    }
    bool Read(std::vector<char> *data, long *time)
    {
        return (reinterpret_cast<ShmData *>(data_ + head_ * size_))->Read(data, time);
    }
};

struct ShmSlice
{
    int attached_;
    pthread_rwlock_t rwlock_;
    pthread_mutex_t mutex_;
    pthread_cond_t cond_;
    char data_[1];

    ShmSlice(const size_t size, const int count, const bool init = false)
    {
        if (init)
        {
            // init rwlock
            pthread_rwlockattr_t rwattr;
            pthread_rwlockattr_init(&rwattr);
            pthread_rwlockattr_setpshared(&rwattr, PTHREAD_PROCESS_SHARED);
            pthread_rwlockattr_setkind_np(&rwattr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
            pthread_rwlock_init(&rwlock_, &rwattr);
            // init mutex
            pthread_mutexattr_t mattr;
            pthread_mutexattr_init(&mattr);
            pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
            pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST); //设置锁历形态
            pthread_mutex_init(&mutex_, &mattr);
            // init condition variable
            pthread_condattr_t cattr;
            pthread_condattr_init(&cattr);
            pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
            pthread_cond_init(&cond_, &cattr);
            // init shm queue
            new (data_) ShmQueue(size, count);
        }
        LockWrite();
        if (init)
        {
            attached_ = 1;
        }
        else
        {
            ++attached_;
        }
        UnlockWrite();
    }
    ~ShmSlice()
    {
        LockWrite();
        const int count = --attached_;
        UnlockWrite();
        if (0 == count)
        {
            pthread_cond_destroy(&cond_);
            pthread_mutex_destroy(&mutex_);
            pthread_rwlock_destroy(&rwlock_);
        }
    }
    int count()
    {
        LockRead();
        const int count = attached_;
        UnlockRead();
        return count;
    }

    void Write(const char *data, const size_t len)
    {
        LockWrite();
        (reinterpret_cast<ShmQueue *>(data_))->Write(data, len);
        UnlockWrite();
    }
    bool Read(std::vector<char> *data, long *time)
    {
        return (reinterpret_cast<ShmQueue *>(data_))->Read(data, time);
    }

    void LockWrite() { if (pthread_rwlock_wrlock(&rwlock_) != 0) printf("pthread_rwlock_wrlock error"); }
    void UnlockWrite() { if (pthread_rwlock_unlock(&rwlock_) != 0) printf("pthread_rwlock_unlock error");}
    void LockRead() {  if (pthread_rwlock_rdlock(&rwlock_) != 0) printf("pthread_rwlock_rdlock error");}
    void UnlockRead() { if (pthread_rwlock_unlock(&rwlock_) != 0) printf("pthread_rwlock_unlock error"); }

    void LockMutex()
    {
        int ret = 0;
        while ((ret = pthread_mutex_lock(&mutex_))!= 0) {
            if (EOWNERDEAD == ret) {
                printf("EOWNERDEAD \n");
                if (pthread_mutex_consistent(&mutex_) != 0) {
                    printf("pthread_mutext_consistent failed.\n");
                }
                if (pthread_mutex_unlock(&mutex_) != 0) {
                    printf("pthread_mutex_unlock failed\n");
                }
                // if (pthread_mutex_lock(&mutex_) != 0) {
                //     printf("pthread_mutex_lock(&mutex_) failed\n");
                // }
            } else {
                printf("pthread_mutex_lock error!\n");
            }
        }
        
    }
    void UnlockMutex() { pthread_mutex_unlock(&mutex_); }
    void NotifyOne() { pthread_cond_signal(&cond_); }
    void NotifyAll() { if (pthread_cond_broadcast(&cond_) != 0) {printf("pthread_cond_broadcast error!\n");}}
    void Wait()
    {
        LockMutex();
        int ret = pthread_cond_wait(&cond_, &mutex_);
        if ( ret != 0) {
            printf("pthread_cond_wait : %d\n", ret);
            printf("ENOTRECOVERABLE:%d, EOWNERDEAD:%d, EPERM:%d, ETIMEDOUT:%d, EINVAL:%d, ", ENOTRECOVERABLE, EOWNERDEAD, EPERM, ETIMEDOUT, EINVAL);
        }
        UnlockMutex();
    }
    bool WaitFor(struct timespec *ts, const std::function<bool()> &cond)
    {
        if (cond && cond())
        {
            return true;
        }
        LockMutex();
        
        int retval = pthread_cond_timedwait(&cond_, &mutex_, ts);
        if ( retval != 0) {
            printf("pthread_cond_wait : %d\n", retval);
            printf("ENOTRECOVERABLE:%d, EOWNERDEAD:%d, EPERM:%d, ETIMEDOUT:%d, EINVAL:%d, ", ENOTRECOVERABLE, EOWNERDEAD, EPERM, ETIMEDOUT, EINVAL);
        }
        UnlockMutex();
        bool ret = false;
        if (cond)
        {
            ret = cond();
        }
        else
        {
            struct timespec now;
            clock_gettime(CLOCK_REALTIME, &now);
            ret = now.tv_sec < ts->tv_sec ||
                  (now.tv_sec == ts->tv_sec && now.tv_nsec <= ts->tv_nsec);
        }
        return ret;
    }
};

class ShmManager
{
public:
    ShmManager(std::string file_name, int size)
        : name_(file_name)
        , size_(sizeof(ShmSlice) + sizeof(ShmQueue) + 3 * (sizeof(ShmData) + size))
    {
        bool init = false;
        // open file descriptor
        int fd = open(name_.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
        if (fd < 0)
        {
            fd = open(name_.c_str(), O_RDWR, 0600);
        }
        else
        {
            // set file size
            struct stat fs;
            fstat(fd, &fs);
            if (fs.st_size < 1)
            {
                ftruncate(fd, size_);
            }
            init = true;
        }
        // mmap
        void *shmaddr = mmap(NULL, size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        new (shmaddr) ShmSlice(size, 3, init);
        slice_ = std::shared_ptr<ShmSlice>(reinterpret_cast<ShmSlice *>(shmaddr), [](ShmSlice *ptr){ ptr->~ShmSlice(); });
        close(fd);
    }

    ~ShmManager()
    {
        running_.store(false);
        slice_->NotifyAll();
        if (read_thread_.joinable())
        {
            read_thread_.join();
        }
        const int count = slice_->count();
        auto ptr = slice_.get();
        slice_.reset();
        if (count > 1)
        {
            printf("count > 1");
            // unmap
            munmap(ptr, size_);
        }
        else
        {
            // remove file
            remove(name_.c_str());
        }
    }

    void Publish(const std::vector<char> &data)
    {
        slice_->Write(data.data(), data.size());
        slice_->NotifyAll();
    }

    void Subscribe(std::function<void(const std::vector<char> &)> callback)
    {
        callback_ = std::move(callback);
        running_ = true;
        read_thread_ = std::thread(&ShmManager::ReadThread, this);
    }

private:
    void ReadThread()
    {
        long read_time = 0;
        while (running_)
        {
            std::vector<char> data;
            long time;
            struct timespec ts;
            clock_gettime(CLOCK_REALTIME, &ts);
            ts.tv_sec += 5;
            if (!slice_->WaitFor(&ts, [&]
                                 { return slice_->Read(&data, &time) && time > read_time; }))
            {
                continue;
            }
            read_time = time;
            // deal with data
            callback_(data);
        }
    }

    std::string name_;
    int size_;
    std::shared_ptr<ShmSlice> slice_;
    std::function<void(const std::vector<char> &)> callback_;
    std::atomic_bool running_;
    std::thread read_thread_;
};

​

pub端实现

publisher.cpp

#include "shm_mgr.h"

int main(int argc, char const *argv[])
{
    auto shm_mgr = std::make_shared<ShmManager>("test", 1024);
    std::vector<char> data;
    for (int i = 0; i < 26; i++) {
        data.push_back('a' + i);
    }
    static uint64_t i = 0;
    while(1) {
        auto tmp(data);
        tmp.push_back(' ');
        auto s = std::to_string(i++);
        tmp.insert(tmp.end(), s.begin(), s.end());
        shm_mgr->Publish(tmp);
        std::cout << i << "\n";
        usleep(200000);
    }
    return 0;
}

sub端实现

subscriber.cpp

#include "shm_mgr.h"
#include <signal.h>

void SigHandle(int signo) {
    printf("catch a signo: %d\n", signo);
    exit(-1);
}

int main(int argc, char const *argv[])
{
    signal(SIGINT, SigHandle);
    signal(SIGSEGV, SigHandle);
    auto shm_mgr = std::make_shared<ShmManager>("test", 1024);
    shm_mgr->Subscribe([](const std::vector<char>& data){
        static uint64_t i = 0;
        std::string temp;
        temp.append(data.begin(), data.end());
        // for (auto c : data) {
        //     std::cout << c ;
        // }
        std::cout << temp <<  std::endl;
    });

    while(1) {
        sleep(1);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值