StarTrack

导航

Linux共享内存环形缓冲区的一种实现

所有的ipc,基本都是为了类似于生产者-消费者的模式来使用,这里给出共享内存环形缓冲区的一种实现。

采用的方法是把写指针放在共享内存的一块区域内,然后读指针去“追赶”写指针。

头文件:

#ifndef SHAREBUFFER_H
#define SHAREBUFFER_H

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include <string>
#include <string.h>
#include <strings.h>
#include <iostream>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <list>
#include <queue>
#include <memory>

#define SHM_PACK_LEN 4096
#define SHM_SIZE 30

#define SHM_PROJ_ID 0x6666
#define SEM_PROJ_ID 0x7777

enum SharedBufferType{
    TypeProducer,
    TypeConsumer
};

enum SharedBufferMode{
    ModeBlock,
    ModeUnblock
};

class SharedBuffer
{
public:
    explicit SharedBuffer(SharedBufferType type, SharedBufferMode mode = ModeBlock);
    virtual ~SharedBuffer();

    int put(const void *buf, const uint len);
    int get(void *buf, const uint len);

private:
    SharedBuffer(const SharedBuffer &);
    SharedBuffer & operator = (const SharedBuffer &);

private:
    typedef struct{
        unsigned int offset;
        unsigned int queueLen;
    }SharedBufferHeadSt;

private:
    int shmAttach();
    int shmDetach();
    int sem_p();
    int sem_v();

private:
    void *m_shmAddr;
    unsigned int m_rdOffset;
    unsigned int m_wrOffset;
    int m_shmId;
    int m_semId;
    key_t m_shmKey;
    key_t m_semKey;
    SharedBufferType m_shmType;
    SharedBufferMode m_shmMode;
};

typedef std::shared_ptr<SharedBuffer> SharedBufferPtr;

#define verbose(fmt, args...)\
{\
    printf("%s-%d: ", __FUNCTION__, __LINE__);\
    printf(fmt, ##args);\
}\

#endif

源文件:

#include "sharedbuffer.h"

SharedBuffer::SharedBuffer(SharedBufferType type, SharedBufferMode mode) :
    m_shmMode(mode),
    m_shmType(type),
    m_shmAddr(nullptr),
    m_shmKey(-1),
    m_semKey(-1),
    m_shmId(-1),
    m_semId(-1),
    m_rdOffset(sizeof(SharedBufferHeadSt)),
    m_wrOffset(sizeof(SharedBufferHeadSt))
{    
    m_shmKey = ftok("/tmp", SHM_PROJ_ID);
    if(m_shmKey < 0){
        printf("SharedBuffer: constructor: m_shmKey failed!\n");
        return;
    }
    printf("m_shmKey[%x]\n", m_shmKey);

    m_semKey = ftok("/tmp", SEM_PROJ_ID);
    if(m_semKey < 0){
        printf("SharedBuffer: constructor: m_semKey failed!\n");
        return;
    }
    printf("m_semKey[%x]\n", m_semKey);

    int ret = shmAttach();
    if(ret < 0){
        printf("SharedBuffer: constructor: shmAttach failed!\n");
        return;
    }
}

SharedBuffer::~SharedBuffer()
{
    int ret = shmDetach();
    if(ret < 0){
        printf("SharedBuffer: destructor: shmDetach failed!\n");
        return;
    }
}

int SharedBuffer::shmAttach()
{
    int shmFlag = 0;
    shmFlag = IPC_CREAT | 0660;   
    int len = sizeof(SharedBufferHeadSt) + SHM_PACK_LEN*SHM_SIZE;
    m_shmId = shmget(m_shmKey, len, shmFlag);
    if(m_shmId < 0){
        perror("SharedBuffer::shmAttach: shmget");
        return -1;
    }

    m_shmAddr = shmat(m_shmId, NULL, 0);
    if(m_shmAddr == (void *)-1){
        perror("SharedBuffer::shmAttach: shmat");
        return -1;
    }

    if(m_shmType == TypeProducer){
        ((SharedBufferHeadSt *)(m_shmAddr))->offset = sizeof(SharedBufferHeadSt);
        ((SharedBufferHeadSt *)(m_shmAddr))->queueLen = 0;
    }

    m_semId = semget(m_semKey, 1, IPC_CREAT | 0660);
    if(m_semId < 0){
        perror("SharedBuffer::shmAttach: semget");
        return -1;
    }

    if(m_shmType == TypeProducer){
        int ret = semctl(m_semId, 0, SETVAL, 1);
        if(ret < 0){
            perror("SharedBuffer::shmAttach: semctl");
            return -1;
        }
    }
    return 0;
}

int SharedBuffer::shmDetach()
{
    int ret = 0;
    if(m_shmType == TypeProducer){
        ret = semctl(m_semId, 1, IPC_RMID);
        if(ret < 0){
            perror("SharedBuffer::shmDetach: semctl");
            return -1;
        }
    }

    ret = shmdt(m_shmAddr);
    m_shmAddr = nullptr;
    if(ret < 0){
        perror("SharedBuffer::shmDetach: shmdt");
        return -1;
    }

    if(m_shmType == TypeProducer){
        ret = shmctl(m_shmId, IPC_RMID, NULL);
        if(ret < 0){
            perror("SharedBuffer::shmDetach: shmctl");
            return -1;
        }
    }

    return 0;
}

int SharedBuffer::sem_p()
{
    short flag = 0;
    if(m_shmMode == ModeUnblock)
        flag = IPC_NOWAIT | SEM_UNDO;
    else
        flag = SEM_UNDO;

    struct sembuf buf = {0, -1, flag};
    int ret = semop(m_semId, &buf, 1);
    if(ret < 0){
        if(errno != EAGAIN)
            perror("semop");
        return -1;
    }
    return 0;
}

int SharedBuffer::sem_v()
{
    short flag = 0;
    if(m_shmMode == ModeUnblock)
        flag = IPC_NOWAIT | SEM_UNDO;
    else
        flag = SEM_UNDO;

    struct sembuf buf = {0, 1, flag};
    int ret = semop(m_semId, &buf, 1);
    if(ret < 0){
        if(errno != EAGAIN)
            perror("semop");
        return -1;
    }
    return 0;
}

int SharedBuffer::put(const void *buf, const uint len)
{
    if(m_shmType != TypeProducer){
        printf("SharedBuffer::put: type invalid!\n");
        return -1;
    }

    if(len > SHM_PACK_LEN || buf == nullptr){
        printf("SharedBuffer::put: invalid parameter!\n");
        return -1;
    }

    int ret = sem_p();
    if(ret < 0)
        return -1;

    m_wrOffset = ((SharedBufferHeadSt *)(m_shmAddr))->offset;
    char *wrPtr = (char*)m_shmAddr + m_wrOffset;
    bzero(wrPtr, SHM_PACK_LEN);
    memcpy(wrPtr, buf, len);
    if(m_wrOffset + SHM_PACK_LEN == (sizeof(SharedBufferHeadSt) + SHM_PACK_LEN*SHM_SIZE)){
        m_wrOffset = sizeof(SharedBufferHeadSt);
        //printf("SharedBuffer::put: overload!\n");
    }else{
        m_wrOffset += SHM_PACK_LEN;
    }
    ((SharedBufferHeadSt *)(m_shmAddr))->offset = m_wrOffset;

    ret = sem_v();
    if(ret < 0)
        return -1;

    return len;
}

int SharedBuffer::get(void *buf, const uint len)
{
    if(m_shmType != TypeConsumer){
        printf("SharedBuffer::get: type invalid!\n");
        return -1;
    }

    if(len < SHM_PACK_LEN || buf == nullptr){
        printf("SharedBuffer::get: invalid parameter!\n");
        return -1;
    }

    int ret = 0;
    if(sem_p() < 0)
        return -1;

    if(m_rdOffset == ((SharedBufferHeadSt *)(m_shmAddr))->offset){
        //printf("SharedBuffer::get: read empty!\n");
        ret = 0;
    }else{
        char *rdPtr = (char*)m_shmAddr + m_rdOffset;
        memcpy(buf, rdPtr, SHM_PACK_LEN);
        ret = SHM_PACK_LEN;
        if(m_rdOffset + SHM_PACK_LEN == (sizeof(SharedBufferHeadSt) + SHM_PACK_LEN*SHM_SIZE)){
            m_rdOffset = sizeof(SharedBufferHeadSt);
        }else{
            m_rdOffset += SHM_PACK_LEN;
        }
    }

    if(sem_v() < 0)
        return -1;

    return ret;
}

 

posted on 2017-06-07 15:52  StarTrack  阅读(1089)  评论(0编辑  收藏  举报