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

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

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

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;      //共享内存IDint 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 shmheadp_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);
}

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

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

.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

Linux IPC实践(13) --System V IPC综合实践相关推荐

  1. Linux进程通信之System V消息队列

    System V消息队列是Open Group定义的XSI,不属于POSIX标准.System V IPC的历史相对很早,在上个世70年代后期有贝尔实验室的分支机构开发,80年代加入System V的 ...

  2. Linux IPC实践(6) --System V消息队列(3)

    消息队列综合案例 消息队列实现回射客户/服务器 server进程接收时, 指定msgtyp为0, 从队首不断接收消息 server进程发送时, 将mtype指定为接收到的client进程的pid cl ...

  3. Linux IPC实践(9) --System V共享内存

    共享内存API #include <sys/ipc.h> #include <sys/shm.h>int shmget(key_t key, size_t size, int ...

  4. System V IPC

    1.概述 System V IPC共有三种类型:System V消息队列.System V 信号量.System V 共享内存区. System V IPC操作函数如下: 2.key_t键和ftok函 ...

  5. system V IPC进程间通信机制一网打尽

    目录 必备IPCS命令解析 ipcs ipcrm Linux IPC消息队列 msgget msgsnd msgrcv msgctl Linux IPC信号量 理解信号量 semget semop s ...

  6. System V IPC POSIX IPC(一):消息队列

    System V IPC & POSIX IPC(一):消息队列 消息队列允许进程之间以消息的形式交换数据,是一种常见的进程之间的通信机制. 1. 消息队列的创建 System V IPC: ...

  7. 【C语言】【unix c】信号量集(system v ipc)

    二.信号量集(system v ipc)信号量集就是数组,数组里的每个元素都是信号量的类型1.获取键值ftok(3)2.使用键值获取信号量集的idsemget(2)#include <sys/t ...

  8. Linux IPC实践(11) --System V信号量(1)

    信号量API #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget ...

  9. 进程间同步---system v ipc 对象信号灯集

    一.信号灯简介 Linux支持System V的信号灯(semaphore),是一种进程间通信的方式,只不过它和管道.FIFO或者共享内存不一样,信号灯主要用于同步或者互斥对共享资源的访问,它的发明来 ...

最新文章

  1. CyberRT使用笔记
  2. 一周焦点 | 李飞飞离职谷歌;Facebook推OCR神器Rosetta;TensorFlow开源新库TFDV
  3. 转载:如何避免代码中的if嵌套
  4. android 两种实现计时器时分秒的实现,把时间放在你的手中~
  5. ggspatial | ggplot2的地图制作拓展包(1):如何添加指北针和比例尺
  6. ffdshow 源代码分析 2: 位图覆盖滤镜(对话框部分Dialog)
  7. 数据科学导论学习小结——其二
  8. 【权限设计】权限系统的设计——由浅至深
  9. 广州坐标系转换大地2000_实用帖 | 从地方坐标系到2000国家大地坐标系的转换方法...
  10. Mac下如何把iphone资料备份到移动硬盘
  11. 计算机网络网桥模拟课程设计,网桥模拟实验
  12. Python面向对象版学员管理系统
  13. BPDU Timers
  14. 联想G40进入BIOS
  15. Linux基础、vim、find命令等
  16. 如何使用ContentProvider打造自己的本地图片库
  17. 【新闻】微信出现大面积BUG!腾讯紧急回应!
  18. matlab中多项式拟合如何给出r方,matlap拟合函数后r^2怎么求
  19. Python实现一个简单的目标检测
  20. linux怎么连接两个服务器,一个服务器端同时创建两个socket连接 linux

热门文章

  1. 计算机网络之物理层:7、物理层设备(中继器、集线器)
  2. Reactor模式和Proactor模式
  3. TensorFlow多层感知机实现MINIST分类
  4. 用Java语言编写的特殊算法
  5. 一个java中HashMap和HashSet的应用实例
  6. Python 编码问题:‘ascii‘ codec can‘t encode characters in position 的解决方案(中文乱码终极解决方案)
  7. from __future__ import absolute_import的作用
  8. C/C++端口复用SO_REUSEADDR(setsockopt参数)
  9. 纯干货:Linux抓包命令集锦(tcpdump)
  10. python 进程池