内存屏障: 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;
#ifndef __SHMFIFO_H__
#define __SHMFIFO_H__
#include
#include
#include
#include
#include
#include
#include
//头信息结构体
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__
#include "shmfifo.h"
#include
#include
#include
#include
#include
#include
#include
#include
//用来初始化信号量的初值的联合体
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);
}
消费者进程
#include
#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);
}
生产者进程
#include "shmfifo.h"
#include
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);
}
}