Linux IPC实践--System V IPC综合实践

实践:实现一个先进先出的共享内存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版本:

  1. //结构体类型定义  
  2. typedef struct shmhead shmhead_t;  
  3. typedef struct shmfifo shmfifo_t;  
  4.   
  5. //共享内存首部定义  
  6. struct shmhead  
  7. {  
  8.     unsigned int blksize;   //块大小  
  9.     unsigned int blocks;    //总块数  
  10.     unsigned int rd_index;  //读索引块  
  11.     unsigned int wr_index;  //写索引块  
  12. };  
  13. //整个shmfifo句柄  
  14. struct shmfifo  
  15. {  
  16.     shmhead_t *p_shm;   //共享内存头部指针  
  17.     char *p_payload;    //有效负载其实地址  
  18.   
  19.     int shmid;      //共享内存ID  
  20.     int sem_mutex;  //互斥信号量  
  21.     int sem_full;   //满信号量  
  22.     int sem_empty;  //空信号量  
  23. };  
  1. /**shmfifo初始化 
  2. 既包含了共享内存的初始化, 也包含了三个信号量的初始化; 
  3. 小技巧: 对一个IPC对象首先尝试打开, 如果打开失败, 则表示该IPC对象尚未创建, 则需要创建之, 而如果打开成功的话, 则进行其他操作 
  4. **/  
  5. shmfifo_t *shmfifo_init(int key, int blksize, int blocks)  
  6. {  
  7.     shmfifo_t *fifo = (shmfifo_t *)malloc(sizeof(shmfifo_t));  
  8.     assert(fifo != NULL);  
  9.     memset(fifo, 0, sizeof(shmfifo_t));  
  10.       
  11.     // 尝试打开共享内存  
  12.     int shmid = shmget(key, 0, 0);  
  13.     // 如果打开失败, 则表示该共享内存尚未创建, 则创建  
  14.     if (shmid == -1)  
  15.     {  
  16.         /** 设置共享内存 **/  
  17.         int size = blksize*blocks + sizeof(shmhead_t);  
  18.         //创建共享内存  
  19.         fifo->shmid = shmget(key, size, IPC_CREAT|0666);  
  20.         if (fifo->shmid == -1)  
  21.             err_exit("shmget error");  
  22.   
  23.         //创建共享内存成功, 则需要将其连接到进程的地址空间  
  24.         //void *shmat(int shmid, const void *shmaddr, int shmflg);  
  25.         fifo->p_shm = (shmhead_t *)shmat(fifo->shmid, NULL, 0);  
  26.         if (fifo->p_shm == (void *) -1)  
  27.             err_exit("shmat error");  
  28.   
  29.         //将共享内存的首部初始化为struct shmhead  
  30.         fifo->p_shm->blksize = blksize;  
  31.         fifo->p_shm->blocks = blocks;  
  32.         fifo->p_shm->rd_index = 0;  
  33.         fifo->p_shm->wr_index = 0;  
  34.   
  35.         fifo->p_payload = (char *)(fifo->p_shm+1);  
  36.   
  37.         /** 设置三个信号量 **/  
  38.         fifo->sem_mutex = sem_create(key);  
  39.         sem_setval(fifo->sem_mutex, 1);  
  40.   
  41.         fifo->sem_full = sem_create(key+1);  
  42.         sem_setval(fifo->sem_full, 10);  
  43.   
  44.         fifo->sem_empty = sem_create(key+2);  
  45.         sem_setval(fifo->sem_empty, 0);  
  46.     }  
  47.     else  
  48.     {  
  49.         fifo->shmid = shmid;  
  50.         //共享内存已经存在, 并且打开成功, 则需要将其连接到进程的地址空间  
  51.         fifo->p_shm = (shmhead_t *)shmat(fifo->shmid, NULL, 0);  
  52.         if (fifo->p_shm == (void *) -1)  
  53.             err_exit("shmat error");  
  54.   
  55.         fifo->p_payload = (char *)(fifo->p_shm+1);  
  56.   
  57.         /** 设置三个信号量 **/  
  58.         fifo->sem_mutex = sem_open(key);  
  59.         fifo->sem_full = sem_open(key+1);  
  60.         fifo->sem_empty = sem_open(key+2);  
  61.     }  
  62.   
  63.     return fifo;  
  64. }  
  1. /**shmfifo的销毁 
  2. 既要销毁共享内存, 也要销毁三个信号量, 还需要将malloc出来的shmfifo_t结构体释放掉 
  3. **/  
  4. void shmfifo_destroy(shmfifo_t *fifo)  
  5. {  
  6.     //释放三个信号量  
  7.     sem_delete(fifo->sem_mutex);  
  8.     sem_delete(fifo->sem_full);  
  9.     sem_delete(fifo->sem_empty);  
  10.     //分离内存  
  11.     shmdt(fifo->p_shm);  
  12.     //删除共享内存  
  13.     if (shmctl(fifo->shmid, IPC_RMID, NULL) == -1)  
  14.         err_exit("remove share memory error");  
  15.     //将fifo内存释放  
  16.     free(fifo);  
  17. }  
  1. /**将buf内容按照顺序写入共享内存 
  2. 注意此处的P,V操作并没有使用SEM_UNDO标记 
  3. **/  
  4. void shmfifo_put(shmfifo_t *fifo, const void *buf)  
  5. {  
  6.     sem_P(fifo->sem_full);  
  7.     sem_P(fifo->sem_mutex);  
  8.   
  9.     //从结构体中获取写入位置  
  10.     char *index = fifo->p_payload +  
  11.                   (fifo->p_shm->wr_index * fifo->p_shm->blksize);  
  12.     memcpy(index, buf, fifo->p_shm->blksize);  
  13.     fifo->p_shm->wr_index = (fifo->p_shm->wr_index+1)%fifo->p_shm->blocks;  
  14.   
  15.     sem_V(fifo->sem_mutex);  
  16.     sem_V(fifo->sem_empty);  
  17. }  
  1. /**将共享内存中的内容按照顺序读出到buf 
  2. 注意此处的P,V操作并没有使用SEM_UNDO标记 
  3. **/  
  4. void shmfifo_get(shmfifo_t *fifo, void *buf)  
  5. {  
  6.     sem_P(fifo->sem_empty);  
  7.     sem_P(fifo->sem_mutex);  
  8.     //从结构体中获取读出位置  
  9.     char *index = fifo->p_payload +  
  10.                   (fifo->p_shm->rd_index * fifo->p_shm->blksize);  
  11.   
  12.     memcpy(buf, index, fifo->p_shm->blksize);  
  13.     fifo->p_shm->rd_index = (fifo->p_shm->rd_index+1)%fifo->p_shm->blocks;  
  14.   
  15.     sem_V(fifo->sem_mutex);  
  16.     sem_V(fifo->sem_full);  
  17. }  
  1. /**测试代码: write.cpp**/  
  2. struct Student  
  3. {  
  4.     char name[32];  
  5.     int age;  
  6. };  
  7.   
  8. int main()  
  9. {  
  10.     shmfifo_t *fifo = shmfifo_init(1234, sizeof(Student), 15);  
  11.   
  12.     Student s;  
  13.     bzero(&s, sizeof(s));  
  14.     strcpy(s.name, "xiaofang");  
  15.     for (int i = 0; i < 15; ++i)  
  16.     {  
  17.         sprintf(&(s.name[8]), "%d", i);  
  18.         s.age = i;  
  19.         shmfifo_put(fifo, &s);  
  20.         cout << "put success" << endl;  
  21.     }  
  22. }  
  1. /**测试代码: read.cpp**/  
  2. int main()  
  3. {  
  4.     shmfifo_t *fifo = shmfifo_init(1234, sizeof(Student), 3);  
  5.   
  6.     Student s;  
  7.     for (int i = 0; i < 5; ++i)  
  8.     {  
  9.         bzero(&s, sizeof(s));  
  10.         shmfifo_get(fifo, &s);  
  11.         printf("name: %s, age = %d\n", s.name, s.age);  
  12.     }  
  13.     return 0;  
  14. }  
  1. /**测试代码: 销毁所创建的共享内存与信号量, free**/  
  2. int main()  
  3. {  
  4.     shmfifo_t *fifo = shmfifo_init(1234, sizeof(Student), 3);  
  5.   
  6.     shmfifo_destroy(fifo);  
  7.     return 0;  
  8. }  

完整C源代码:http://download.csdn.net/detail/hanqing280441589/8437855

 

C++版本:

  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. };  
  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. }  
  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. }  
  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. }  
  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. }  

完整C++源代码:http://download.csdn.net/download/hanqing280441589/8438025

 

附-Makefile, 两个程序都可以使用该文件

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
  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  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值