共享内存环形队列shm_ring_queue(内存屏障)

内存屏障https://blog.csdn.net/caoshangpa/article/details/78853919

读内核代码进一步学习 Memory Barrier 的使用。
Linux 内核实现的无锁(只有一个读线程和一个写线程时)环形缓冲区 kfifo 就使用到了 Memory Barrier



共享内存环形队列的功能介绍:
1、共享内存的操作:创建/获取、映射、反映射、删除
2、无锁环形队列,本文(支持多生产者多消费者模型)
3、若想(支持单生产者单消费者模型),可以将“信号量”、“互斥锁”的相关部分代码注释; 还要添加2个接口,判断空和判断满
4、可以在head_t中添加一个成员size,表示当前队列中块的个数,用于判断队列空和队列满

question:为什么要使用共享内存?
答案:因为要支持“进程间通信”!


实现核心点:
(1) 共享内存的第一个位置,存放环形队列的信息,用head_t结构体表示
    当进程向共享内存的环形队列中存取数据时,首先查看head_t,即找到读写位置,判断是否空满,之后再存取
(2) 共享内存head_t后面的所有内容,都是环形队列,用p_payload表示
    当向共享内存存取数据时,获取到head_t后,操作的都是p_payload指针
(3)p_payload是指向真正存取数据的缓冲区,类型可以是void*么?
答案:不行,必须设置为char,因为后面指针p_payload的增加操作来定位存取位置(需要用 fifo->p_payload+(fifo->p_head->wr_idx/wr_idx * fifo->p_head->blksz ),将类型设置为char*,增加的步长是1
(4)fifo->p_head->blksz      // 块大小的妙用
    表示环形队列中存放一个数据块ELEM的大小 ==> 采用blksz变量,可以不用预先知道环形队列中存放什么类型的数据块,只需要在使用时知道存放数据块的尺寸即可。简单的说,不用在shmfifo之前 [定义/声明] 结构体:

typedef struct{  // 提前 [定义/声明] 结构体ELEM
	... 
	...
}ELEM;

typedef struct shmfifo
{
    head_t    *p_head;
    ELEM*     p_payload; // 类型是已经知道的结构体
    int shmid;
    int sem_full;
    int sem_empty;
    int sem_mutex;
}shmfifo_t;

(1)shmfifo.h

#ifndef __SHMFIFO_H__
#define __SHMFIFO_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <assert.h>
#include <sys/ipc.h>
#include <unistd.h>
//头信息结构体
typedef struct shm_head
{
    int rd_idx;  //读位置
    int wr_idx;  //写位置
    int blocks;  //块的总数capacitu
    int blksz;   //每块的大小sizeof(ELEM)
}head_t;

//总的这块用来实现消息队列的 共享内存结构体
typedef struct shmfifo
{
    head_t *p_head;//指向头信息结构体的指针
    char*  p_payload;//装有效内容的起始地址
    int shmid;
    int sem_full;//还有多少可以装的信号量
    int sem_empty;//还有多少可以消费的信号量
    int sem_mutex;//不能2个用户同时访问一个位置的 互斥信号量
}shmfifo_t;

//初始化以上结构体的函数,
//返回类型为那块共享内存(共享内存结构体)的地址
//参数为:共享内存的key,要申请的块数,每块的大小
shmfifo_t* shmfifo_init(int key,int blocks,int blksz);

//往这块共享内存放数据
//参数为:申请的共享内存的地址,要放的数据的源地方
void shmfifo_put(shmfifo_t* fifo, const void* buf);

//从这块共享内存取数据
//参数为:要取的共享内存地址,取到的数据暂放的地方
void shmfifo_get(shmfifo_t* fifo, void* buf);

//销毁申请的这块共享内存
//参数为:要销毁的共享内存的地址
void shmfifo_destroy(shmfifo_t* fifo);

#if 0
/**
*    环形队列,判满
*@param [in]    p_fifo_idx   队列头部
*@return   满,1;不满,0
*/
int is_full(fifo_t *p_fifo_idx)
{
    if ((p_fifo_idx->rq_head.wr_index + 1) % (p_fifo_idx->rq_head.fifo_capacity) == p_fifo_idx->rq_head.rd_index)
        return 1;
    else
        return 0;
}

/**
*    环形队列,判空
*@param [in]    p_fifo_idx   队列头部
*@return   空,1;不空,0
*/
int is_empty(fifo_t *p_fifo_idx)
{
    if (p_fifo_idx->rq_head.rd_index == p_fifo_idx->rq_head.wr_index) // 空
        return 1;
    else
        return 0;
}
#endif


#endif //__SHMFIFO_H__

(2)shmfifo.c

 #include "shmfifo.h"
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#include <assert.h>

//用来初始化信号量的初值的联合体
union semun
{
    int value;
};

//p操作
static void p(int id)
{
    struct sembuf sb[1];
    sb[0].sem_num=0;
    sb[0].sem_op=-1;
}
static void v(int id)
{
    struct sembuf sb[1];
    sb[0].sem_num=0;
    sb[0].sem_op=1;
    sb[0].sem_flg=0;
}

//初始化以上结构体的函数,
//返回类型为那块共享内存(共享内存结构体)的地址
//参数为:共享内存的key,要申请的块数,每块的大小
shmfifo_t* shmfifo_init(int key,int blocks,int blksz){
    //1.初始化
    //向内存申请一块(共享内存结构体大小)的空间,用一个这种结构体类型指针保存其地址
    shmfifo_t *p=(shmfifo_t*)malloc(sizeof(shmfifo_t));
    assert(p);

    //计算要创建的共享内存大小,记得加上头部信息结构体部分
    int len = blocks * blksz + sizeof(head_t);

    int shmid = shmget(key, len, 0);//打开len大小的共享内存key

    if(shmid == -1)//共享内存不存在,则创建
    {
        shmid = shmget(key, len, IPC_CREAT|0644);
        if(shmid == -1)//创建失败
        {
            perror("shmget failure\n");
            exit(1);
        }
        else//创建成功,则初始化共享内存结构体,p指向这个结构体
        {
            //读写位置初始化为0 
            p->p_head->rd_idx=0;
            p->p_head->wr_idx=0;
            //块数量、块大小由使用者传递过来
            p->p_head->blocks=blocks;
            p->p_head->blksz=blksz;
            //指向真正内容的起始位置,即跳过头部信息
            p->p_payload=(char*)(p->p_head+1);
            p->shmid=shmid;
            p->sem_empty=semget(key,1,IPC_CREAT|0644);
            p->sem_full=semget(key+1,1,IPC_CREAT|0644);
            p->sem_mutex=semget(key+2,1,IPC_CREAT|0644);

            //用来初始化信号量初值的联合体
            union semun su;
            semctl(p->sem_empty, 0, SETVAL, su);
            su.value=blocks;
            semctl(p->sem_full, 0, SETVAL, su);

            //将sem_mutex信号量初始值设置为1(允许一个用户进来)
            su.value=1;
            semctl(p->sem_mutex, 0, SETVAL, su);
        }
    }
    else//共享内存存在,那么只需要初始化部分数据
    {
        //存在的话,将共享内存挂载
        p->p_head=(head_t*)shmat(shmid,NULL,0);
        //打开的话不用给信号量设定初值,由于是打开,后2个参数都是0
        p->sem_empty=semget(key+2,0,0);
    }
    return p;
}

//向队列插入数据(放数据):将buf指向的内容,拷贝到wri_idx
void shmfifo_put(shmfifo_t* fifo, const void* buf)
{
    //可装的资源减1
    p(fifo->sem_full);
    //保证只有一个操作
    p(fifo->sem_mutex);
    //放数据
    memcpy(fifo->p_payload+(fifo->p_head->wr_idx*fifo->p_head->blksz), buf, fifo->p_head->blksz);
    //写下标后移
    fifo->p_head->wr_idx=(fifo->p_head->wr_idx+1)%(fifo->p_head->blocks);
    //释放资源
    v(fifo->sem_mutex);
    v(fifo->sem_empty);
}

//从队列弹出数据(取数据):将wri_idx指向的内容,拷贝到buf
void shmfifo_get(shmfifo_t* fifo, void* buf)
{
    p(fifo->sem_mutex);
    //可消费的资源减1
    p(fifo->sem_empty);
    //保证只有一个操作
    //取数据
    memcpy(buf,fifo->p_payload+(fifo->p_head->rd_idx*fifo->p_head->blksz),fifo->p_head->blksz);
    //写下标后移
    fifo->p_head->rd_idx=(fifo->p_head->rd_idx+1)%(fifo->p_head->blocks);
    //释放资源
    v(fifo->sem_full);
    v(fifo->sem_mutex);
}
//销毁共享内存
void shmfifo_destroy(shmfifo_t* fifo)
{
    //先卸载(即断开连接)
    shmdt(fifo->p_head);
    //销毁(即删除)
    shmctl(fifo->shmid, IPC_RMID, 0);
    semctl(fifo->sem_full, 0, IPC_RMID);
    semctl(fifo->sem_mutex, 0, IPC_RMID);
    semctl(fifo->sem_empty, 0, IPC_RMID);
    free(fifo);
}

(3)get.c

消费者进程

#include <stdio.h>
#include "shmfifo.h"

typedef struct
{
    int age;
    char name[32];
}person_t;


int main()
{
    shmfifo_t *fifo = shmfifo_init(1234,3,sizeof(person_t));
    person_t person;

    int i=0;
    for(i=0;i<10;i++)
    {
        shmfifo_get(fifo,&person);
        printf("name=%s, age=%d\n", person.name, person.age);
        sleep(1);
    }
    shmfifo_destroy(fifo);
}

(4)put.c

生产者进程

 #include "shmfifo.h"
 #include <stdio.h>
 
 typedef struct
 {
     int age;
     char name[32];
 }person_t;
 
 int main()
 {
 
     shmfifo_t *fifo=shmfifo_init(1234,3,sizeof(person_t));
     person_t person;
 
     int i=0;
     for(i=0;i<10;i++)
     {
         person.age=10+i;
         sprintf(person.name,"name:%d",i+1);
         shmfifo_put(fifo,&person);
         printf("put %d\n",i+1);
     }
 }
  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值