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 [email protected]
- write: write.o shmfifo.o ipc.o
- $(CC) $(CPPFLAGS) $^ -lrt -o [email protected]
- read: read.o shmfifo.o ipc.o
- $(CC) $(CPPFLAGS) $^ -lrt -o [email protected]
- free: free.o shmfifo.o ipc.o
- $(CC) $(CPPFLAGS) $^ -lrt -o [email protected]
- clean:
- -rm -rf $(BIN) *.o bin/ obj/ core
需要实时链接库,只需要修改Makefile文件,加上-lrt就可以了