Linux IPC实践(13) --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版本:
//结构体类型定义 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; //共享内存ID int 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 shmhead fifo->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; //共享内存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); }
完整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综合实践的更多相关文章
- linux进程间通讯-System V IPC 信号量
进程间通信的机制--信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的很多其它内容,能够阅读我的还有一篇文章:Linux进程间通信--使用信号.以下就进入信号量的 ...
- Linux 系统编程 学习:04-进程间通信2:System V IPC(1)
Linux 系统编程 学习:04-进程间通信2:System V IPC(1) 背景 上一讲 进程间通信:Unix IPC-信号中,我们介绍了Unix IPC中有关信号的概念,以及如何使用. IPC的 ...
- 四十九、进程间通信——System V IPC 之消息队列
49.1 System V IPC 介绍 49.1.1 System V IPC 概述 UNIX 系统存在信号.管道和命名管道等基本进程间通讯机制 System V 引入了三种高级进程间通信机制 消息 ...
- 《Unix网络编程》卷2 读书笔记 第3章- System V IPC
1. 概述 三种类型的System V IPC:System V 消息队列.System V 信号量.System V 共享内存区 System V IPC在访问它们的函数和内核为它们维护的信息上共享 ...
- UNIX 进程间通讯(IPC)概念(Posix,System V IPC)
IPC(Inter-Process Communication,进程间通讯)可以有三种信息共享方式(随文件系统,随内核,随共享内存).(当然这里虽然说是进程间通讯,其实也是可以和线程相通的). 相对 ...
- Linux进程通信之System V消息队列
System V消息队列是Open Group定义的XSI,不属于POSIX标准.System V IPC的历史相对很早,在上个世70年代后期有贝尔实验室的分支机构开发,80年代加入System V的 ...
- Linux IPC实践(9) --System V共享内存
共享内存API #include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int ...
- Linux IPC实践(6) --System V消息队列(3)
消息队列综合案例 消息队列实现回射客户/服务器 server进程接收时, 指定msgtyp为0, 从队首不断接收消息 server进程发送时, 将mtype指定为接收到的client进程的pid ...
- Linux 系统编程 学习:05-进程间通信2:System V IPC(2)
Linux 系统编程 学习:05-进程间通信2:System V IPC(2) 背景 上一讲 进程间通信:System V IPC(1)中,我们介绍了System IPC中有关消息队列.共享内存的概念 ...
随机推荐
- Windows无法安装到这个磁盘
今天手动装系统的时候出现以下这样的错误, 请看图: 进入BIOS F9 Setup Defaults ,初始化恢复 1.在进行windows安装分区时, 磁盘分区界面无法继续进行,出现" ...
- SAS中常见的数组函数
SAS中常见的数组函数有: dim dimk hbound hboundk lbound lboundk 数组函数计萁数组的维数.上下界,有利于写出可移植的程序,数组函数包括:dim(x) 求数组x第 ...
- Python中的赋值(复制)、浅拷贝、深拷贝之间的区别
1.赋值: 只是复制了新对象的引用,不会开辟新的内存空间. 2.浅拷贝: 创建新对象,其内容是原对象的引用. 浅拷贝有三种形式:切片操作,工厂函数,copy模块中的copy函数. 如: ...
- python中的函数(定义、多个返回值、默认参数、参数组)
函数定义 在python中函数的定义以及调用如下代码所示: def test(x): y = x+1 return y result = test(2) print(result) 多个返回值的情况 ...
- C++笔记十二:C++对C的扩展——struct关键字类型增强
C语言的struct定义了一组变量的集合,C编译器并不认为这是一种新的类型. C++中的struct是一个新类型的定义声明. struct Student { char name[100]; int ...
- node安装教程
推荐安装教程博客: https://www.cnblogs.com/zhouyu2017/p/6485265.html
- docker环境 快速使用elasticsearch-head插件
docker环境 快速使用elasticsearch-head插件 #elasticsearch配置 #进入elk容器 docker exec -it elk /bin/bash #head插件访问配 ...
- 实验与作业(Python)-文件操作
1.CSV文件的处理 下载-身份证号文件 导入: 读入"身份证号.txt",然后打印出来.注意:是否多打了一行,为什么? 读入"身份证号.txt",然后存储到& ...
- Unity使用UGUI进行VR游戏的界面开发
原文链接:http://gad.qq.com/article/detail/7181505 本文首发腾讯GAD开发者平台,未经允许,不得转载 我不知道有多少同学是跟我一样,在开发VR游戏中,是使用的面 ...
- JFinal中使用QuartzPlugin报ClassCastException解决方法
JDK1.8中泛型反射修改对旧版本的影响 本文地址:http://blog.csdn.net/sushengmiyan 本文作者:苏生米沿 问题复现环境: JDK1.8 JFinal1.9 quart ...