Linux进程间通信(IPC)编程实践(十一)System V信号量---实现一个先进先出的共享内存shmfifo


使用消息队列即可实现消息的先进先出(FIFO), 但是使用共享内存实现消息的先进先出则更加快速;

我们首先完成C语言版本的shmfifo(基于过程调用), 然后在此基础上实现C++版本的ShmFifo, 将1块共享内存与3个信号量(1个mutext信号量, 1个full信号量, 1个empty信号量)封装成一个类ShmFifo, 然后编写各自的测试代码;

 

shmfifo说明:

将申请到的共享内存作为一块缓冲区, 将该内存的首部(p_shm到p_payload的内容)格式化为如上图所示的形式;

读/写进程不断的按照现金先出的原则从其中读出/写入数据, 则读/写进程就可以看成生产者/消费者了, 因此,使用信号量sem_mutex(初值为1)来互斥访问共享内存, 使用sem_full(初值为共享缓冲区块数), sem_empty(初值为0)来同步两个进程.

 

\

我们使用C++来实现:

 

//ShmFifo类设计  
class ShmFifo  
{  
public:  
    ShmFifo(int _key, int _blksize, int _blocks);  
    ~ShmFifo();  
    void put(const void *buf);  
    void get(void *buf);  
    void destroy();  
  
private:  
    typedef struct shmhead  
    {  
        unsigned int blksize;   //块大小  
        unsigned int blocks;    //总块数  
        unsigned int rd_index;  //读索引块  
        unsigned int wr_index;  //写索引块  
    } shmhead_t;  
  
private:  
    shmhead_t *p_shm;   //共享内存头部指针  
    char *p_payload;    //有效负载其实地址  
  
    int shmid;      //共享内存ID  
    int sem_mutex;  //互斥信号量  
    int sem_full;   //满信号量  
    int sem_empty;  //空信号量  
};  

/** 构造函数 **/  
ShmFifo::ShmFifo(int _key, int _blksize, int _blocks)  
{  
    // 打开一块共享内存  
    shmid = shmget(_key, 0, 0);  
    // 如果打开失败, 则表示该共享内存尚未创建, 则创建之  
    if (shmid == -1)  
    {  
        /** 设置共享内存 **/  
        int size = _blksize*_blocks + sizeof(shmhead_t);  
        //创建共享内存  
        shmid = shmget(_key, size, IPC_CREAT|0666);  
        if (shmid == -1)  
            err_exit("shmget error");  
  
        //创建共享内存成功, 则需要将其连接到进程的地址空间  
        p_shm = (shmhead_t *)shmat(shmid, NULL, 0);  
        if (p_shm == (void *) -1)  
            err_exit("shmat error");  
  
        //将共享内存的首部初始化为struct shmhead  
        p_shm->blksize = _blksize;  
        p_shm->blocks = _blocks;  
        p_shm->rd_index = 0;  
        p_shm->wr_index = 0;  
  
        p_payload = (char *)(p_shm+1);  
  
        /** 设置三个信号量 **/  
        sem_mutex = sem_create(_key);  
        sem_setval(sem_mutex, 1);  
  
        sem_full = sem_create(_key+1);  
        sem_setval(sem_full, _blocks);  
  
        sem_empty = sem_create(_key+2);  
        sem_setval(sem_empty, 0);  
    }  
    else  
    {  
        //共享内存已经存在, 并且打开成功, 则只需需将其连接到进程的地址空间  
        p_shm = (shmhead_t *)shmat(shmid, NULL, 0);  
        if (p_shm == (void *) -1)  
            err_exit("shmat error");  
  
        p_payload = (char *)(p_shm+1);  
  
        /** 打开三个信号量 **/  
        sem_mutex = sem_open(_key);  
        sem_full = sem_open(_key+1);  
        sem_empty = sem_open(_key+2);  
    }  
}  
  
/** 析构函数 **/  
ShmFifo::~ShmFifo()  
{  
    shmdt(p_shm);   //将共享内存卸载  
    p_shm = NULL;  
    p_payload = NULL;  
}  

/** destroy函数 **/  
void ShmFifo::destroy()  
{  
    sem_delete(sem_mutex);  
    sem_delete(sem_full);  
    sem_delete(sem_empty);  
    if (shmctl(shmid, IPC_RMID, NULL) == -1)  
        err_exit("remove share memory error");  
}  

/** put函数 **/  
void ShmFifo::put(const void *buf)  
{  
    sem_P(sem_full);  
    sem_P(sem_mutex);  
  
    /** 进入临界区 **/  
    //从结构体中获取写入位置  
    char *index = p_payload +  
                  (p_shm->wr_index * p_shm->blksize);  
    //写入  
    memcpy(index, buf, p_shm->blksize);  
    //index后移  
    p_shm->wr_index = (p_shm->wr_index+1)%p_shm->blocks;  
    /** 退出临界区 **/  
  
    sem_V(sem_mutex);  
    sem_V(sem_empty);  
}  

/** get函数 **/  
void ShmFifo::get(void *buf)  
{  
    sem_P(sem_empty);  
    sem_P(sem_mutex);  
  
    /** 进入临界区 **/  
    //从结构体中获取读出位置  
    char *index = p_payload +  
                  (p_shm->rd_index * p_shm->blksize);  
    //读取  
    memcpy(buf, index, p_shm->blksize);  
    p_shm->rd_index = (p_shm->rd_index+1)%p_shm->blocks;  
    /** 退出临界区 **/  
  
    sem_V(sem_mutex);  
    sem_V(sem_full);  
}  

.PHONY: clean all  
CC = g++  
CPPFLAGS = -Wall -g  
BIN = write read free  
SOURCES = $(BIN.=.cpp)  
all: $(BIN)  
  
%.o: %.cpp  
    $(CC) $(CPPFLAGS) -c $^ -o $@  
  
write: write.o shmfifo.o ipc.o  
    $(CC) $(CPPFLAGS) $^ -lrt -o $@  
read: read.o shmfifo.o ipc.o  
    $(CC) $(CPPFLAGS) $^ -lrt -o $@  
  
free: free.o shmfifo.o ipc.o  
    $(CC) $(CPPFLAGS) $^ -lrt -o $@  
  
clean:  
    -rm -rf $(BIN) *.o bin/ obj/ core  

 

需要实时链接库,只需要修改Makefile文件,加上-lrt就可以了

 

相关内容