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中有关消息队列.共享内存的概念 ...
随机推荐
- Python自动化测试入门
在当前自动化测试中,最火的语言就是Python,很容易上手.然后就是Java+testng+appium做的UI自动化测试.下面我们就用Python脚本,做自动化集成测试. (1)获取APK文件中ID ...
- Node.js 加密
稳定性: 2 - 不稳定; 正在讨论未来版本的 API 改进,会尽量减少重大变化.详见后文. 使用 require('crypto') 来访问这个模块. 加密模块提供了 HTTP 或 HTTPS 连接 ...
- Django URL (路由系统)
URL配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL模式以及要为该URL模式调用的视图函数之间的映射表:你就是以这种方式告诉Django,对于这个URL调用这段代码,对于那 ...
- Linux(八)用户管理
8.1 基本介绍 Linux系统是一个多用户多任务的操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账户,然后以这个账户身份进入系统. Linux的用户需要至少要属于一个组. 8 ...
- B+树在磁盘存储中的应用
欢迎探讨,如有错误敬请指正 如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 我们首先提一个问题, B+树比平衡二叉树在索引数据方面要快么? 大多数人可能认为肯定还是 ...
- ZooKeeper之(五)集群管理
在一台机器上运营一个ZooKeeper实例,称之为单机(Standalone)模式.单机模式有个致命的缺陷,一旦唯一的实例挂了,依赖ZooKeeper的应用全得完蛋. 实际应用当中,一般都是采用集群模 ...
- 自写JQ控件-树状菜单控件[demo下载]
一个多月没有写博客了,最近也弄一个基于JQ的树状菜单控件,在此分享给大家.另外呢,通过这个例子分享一下怎么写JQ控件的. 事实上工作中,也是经常遇到的,有些时候自己想实现一些前端效果,用网上一些插件吧 ...
- 20160215.CCPP体系详解(0025天)
程序片段(01):01.Malloc.c 内容概要:Malloc拓展 #include <stdio.h> #include <stdlib.h> //01.内存伸缩函数: / ...
- Android简易实战教程--第四十四话《ScrollView和HorizontalScrollView简单使用》
一.ScrollView 由于手机屏幕的高度有限,当普通布局放不下现实和的内容时,ScrollView视图(滚动视图)就会派上用场,因为数据可以往下滚动显示. 二.HorizontalScrollVi ...
- ROS机器人程序设计(原书第2版)补充资料 (壹) 第一章 ROS系统入门
ROS机器人程序设计(原书第2版)补充资料 (壹) 第一章 ROS系统入门 书中,大部分出现hydro的地方,直接替换为indigo或jade或kinetic,即可在对应版本中使用. 第一章主要包括R ...