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++来实现:

[cpp]  view plain  copy
  1. //ShmFifo类设计    
  2. class ShmFifo    
  3. {    
  4. public:    
  5.     ShmFifo(int _key, int _blksize, int _blocks);    
  6.     ~ShmFifo();    
  7.     void put(const void *buf);    
  8.     void get(void *buf);    
  9.     void destroy();    
  10.     
  11. private:    
  12.     typedef struct shmhead    
  13.     {    
  14.         unsigned int blksize;   //块大小    
  15.         unsigned int blocks;    //总块数    
  16.         unsigned int rd_index;  //读索引块    
  17.         unsigned int wr_index;  //写索引块    
  18.     } shmhead_t;    
  19.     
  20. private:    
  21.     shmhead_t *p_shm;   //共享内存头部指针    
  22.     char *p_payload;    //有效负载其实地址    
  23.     
  24.     int shmid;      //共享内存ID    
  25.     int sem_mutex;  //互斥信号量    
  26.     int sem_full;   //满信号量    
  27.     int sem_empty;  //空信号量    
  28. };    

[cpp]  view plain  copy
  1. /** 构造函数 **/    
  2. ShmFifo::ShmFifo(int _key, int _blksize, int _blocks)    
  3. {    
  4.     // 打开一块共享内存    
  5.     shmid = shmget(_key, 0, 0);    
  6.     // 如果打开失败, 则表示该共享内存尚未创建, 则创建之    
  7.     if (shmid == -1)    
  8.     {    
  9.         /** 设置共享内存 **/    
  10.         int size = _blksize*_blocks + sizeof(shmhead_t);    
  11.         //创建共享内存    
  12.         shmid = shmget(_key, size, IPC_CREAT|0666);    
  13.         if (shmid == -1)    
  14.             err_exit("shmget error");    
  15.     
  16.         //创建共享内存成功, 则需要将其连接到进程的地址空间    
  17.         p_shm = (shmhead_t *)shmat(shmid, NULL, 0);    
  18.         if (p_shm == (void *) -1)    
  19.             err_exit("shmat error");    
  20.     
  21.         //将共享内存的首部初始化为struct shmhead    
  22.         p_shm->blksize = _blksize;    
  23.         p_shm->blocks = _blocks;    
  24.         p_shm->rd_index = 0;    
  25.         p_shm->wr_index = 0;    
  26.     
  27.         p_payload = (char *)(p_shm+1);    
  28.     
  29.         /** 设置三个信号量 **/    
  30.         sem_mutex = sem_create(_key);    
  31.         sem_setval(sem_mutex, 1);    
  32.     
  33.         sem_full = sem_create(_key+1);    
  34.         sem_setval(sem_full, _blocks);    
  35.     
  36.         sem_empty = sem_create(_key+2);    
  37.         sem_setval(sem_empty, 0);    
  38.     }    
  39.     else    
  40.     {    
  41.         //共享内存已经存在, 并且打开成功, 则只需需将其连接到进程的地址空间    
  42.         p_shm = (shmhead_t *)shmat(shmid, NULL, 0);    
  43.         if (p_shm == (void *) -1)    
  44.             err_exit("shmat error");    
  45.     
  46.         p_payload = (char *)(p_shm+1);    
  47.     
  48.         /** 打开三个信号量 **/    
  49.         sem_mutex = sem_open(_key);    
  50.         sem_full = sem_open(_key+1);    
  51.         sem_empty = sem_open(_key+2);    
  52.     }    
  53. }    
  54.     
  55. /** 析构函数 **/    
  56. ShmFifo::~ShmFifo()    
  57. {    
  58.     shmdt(p_shm);   //将共享内存卸载    
  59.     p_shm = NULL;    
  60.     p_payload = NULL;    
  61. }    

[cpp]  view plain  copy
  1. /** destroy函数 **/    
  2. void ShmFifo::destroy()    
  3. {    
  4.     sem_delete(sem_mutex);    
  5.     sem_delete(sem_full);    
  6.     sem_delete(sem_empty);    
  7.     if (shmctl(shmid, IPC_RMID, NULL) == -1)    
  8.         err_exit("remove share memory error");    
  9. }    

[cpp]  view plain  copy
  1. /** put函数 **/    
  2. void ShmFifo::put(const void *buf)    
  3. {    
  4.     sem_P(sem_full);    
  5.     sem_P(sem_mutex);    
  6.     
  7.     /** 进入临界区 **/    
  8.     //从结构体中获取写入位置    
  9.     char *index = p_payload +    
  10.                   (p_shm->wr_index * p_shm->blksize);    
  11.     //写入    
  12.     memcpy(index, buf, p_shm->blksize);    
  13.     //index后移    
  14.     p_shm->wr_index = (p_shm->wr_index+1)%p_shm->blocks;    
  15.     /** 退出临界区 **/    
  16.     
  17.     sem_V(sem_mutex);    
  18.     sem_V(sem_empty);    
  19. }    

[cpp]  view plain  copy
  1. /** get函数 **/    
  2. void ShmFifo::get(void *buf)    
  3. {    
  4.     sem_P(sem_empty);    
  5.     sem_P(sem_mutex);    
  6.     
  7.     /** 进入临界区 **/    
  8.     //从结构体中获取读出位置    
  9.     char *index = p_payload +    
  10.                   (p_shm->rd_index * p_shm->blksize);    
  11.     //读取    
  12.     memcpy(buf, index, p_shm->blksize);    
  13.     p_shm->rd_index = (p_shm->rd_index+1)%p_shm->blocks;    
  14.     /** 退出临界区 **/    
  15.     
  16.     sem_V(sem_mutex);    
  17.     sem_V(sem_full);    
  18. }    

[cpp]  view plain  copy
  1. .PHONY: clean all    
  2. CC = g++    
  3. CPPFLAGS = -Wall -g    
  4. BIN = write read free    
  5. SOURCES = $(BIN.=.cpp)    
  6. all: $(BIN)    
  7.     
  8. %.o: %.cpp    
  9.     $(CC) $(CPPFLAGS) -c $^ -o $@    
  10.     
  11. write: write.o shmfifo.o ipc.o    
  12.     $(CC) $(CPPFLAGS) $^ -lrt -o $@    
  13. read: read.o shmfifo.o ipc.o    
  14.     $(CC) $(CPPFLAGS) $^ -lrt -o $@    
  15.     
  16. free: free.o shmfifo.o ipc.o    
  17.     $(CC) $(CPPFLAGS) $^ -lrt -o $@    
  18.     
  19. clean:    
  20.     -rm -rf $(BIN) *.o bin/ obj/ core    

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

猜你喜欢

转载自blog.csdn.net/zjy900507/article/details/80204744
今日推荐