最近项目中需要用到共享内存通信,为了方便操作,加入了一种ring队列数据结构。我本人也是第一次接触ring队列,于是在
网上找相关资料,主要是看了以下几个帖子:
https://blog.csdn.net/ds1130071727/article/details/85772923
https://www.cnblogs.com/java-ssl-xy/p/7868531.html
https://blog.csdn.net/yusiguyuan/article/details/19906363
通过以上的几个帖子主要是了解了ring这种数据结构,并且自已也写了小demo试了一下,着重看了ring的入队和出队的函数操作。
说实话,只是根据注释在逻辑上做了了解并没有过多地研究为什么这样就能完美地实现......
我的需求是简单来说是这样的:两个完全没有关系的进程A、B,A是生产者,B是消费者,需要通过共享内存来获取数据(姑且先不考虑进程间的同步问题)。
我的思路:
(1):ring队列中的buffer是实际存储数据的地方,因此buffer实际上应该指向实际创建的共享内存;
(2):出队列和入队列的操作都需要知道队列的起始地址,也就是队列头,但是无论是哪边创建出队列头,另一方应该怎么获取队列呢?
我的想法:干脆队列头指向创建出的共享内存,然后通过指针偏移来让ring->buffer指向共享内存中除头所占的空间。如图:
经我修改后的ring.h如下
#ifndef RING_H__
#define RING_H__
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
struct ring_buffer
{
int size; //大小
int in; //入口位置
int out; //出口位置
void *buffer; //缓冲区
};
#define RING_SIZE (sizeof(struct ring_buffer))
#define RING_BUFFER_SIZE (1024 * 1024)
#define SHM_SIZE (RING_SIZE + RING_BUFFER_SIZE)
//判断x是否是2的次方
#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0))
//取a和b中最小值
#define min(a, b) (((a) < (b)) ? (a) : (b))
struct ring_buffer* ring_buffer_init(void *head,int buf_size)
{
struct ring_buffer *ring_buf = NULL;
if(!is_power_of_2(buf_size)){
fprintf(stderr,"size must be power of 2.\n");
return NULL;
}
/*
ring_buf = (struct ring_buffer *)malloc(sizeof(struct ring_buffer));
if (!ring_buf)
{
fprintf(stderr,"Failed to malloc memory,errno:%u,reason:%s",errno, strerror(errno));
return ring_buf;
}*/
ring_buf = (struct ring_buffer *)head;
ring_buf->size = buf_size;
ring_buf->in = 0;
ring_buf->out = 0;
ring_buf->buffer = head + RING_SIZE;
return ring_buf;
}
void ring_buffer_free(struct ring_buffer *ring_buf)
{
if (ring_buf)
{
printf("do nothing");
return;
}
}
//缓冲区的长度
int __ring_buffer_len(const struct ring_buffer *ring_buf)
{
return (ring_buf->in - ring_buf->out);
}
//从缓冲区中取数据
int ring_buffer_get(struct ring_buffer *ring_buf, void * buffer, int size)
{
if(!ring_buf || !buffer){
printf("ring_buf or buffer is null\n");
return 0;
}
if(ring_buf->out == ring_buf->in){
printf("ring is empty\n");
return 0;
}
int len = 0;
size = min(size, ring_buf->in - ring_buf->out);
/* first get the data from fifo->out until the end of the buffer */
len = min(size, ring_buf->size - (ring_buf->out & (ring_buf->size - 1)));
memcpy(buffer, ring_buf->buffer + (ring_buf->out & (ring_buf->size - 1)), len);
/* then get the rest (if any) from the beginning of the buffer */
memcpy(buffer + len, ring_buf->buffer, size - len);
ring_buf->out += size;
return size;
}
//向缓冲区中存放数据
int ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, int size)
{
if(ring_buf->size == ring_buf->out - ring_buf->in){
printf("ring is full\n");
return 0;
}
int len = 0;
size = min(size, ring_buf->size - ring_buf->in + ring_buf->out);
/* first put the data starting from fifo->in to buffer end */
len = min(size, ring_buf->size - (ring_buf->in & (ring_buf->size - 1)));
memcpy(ring_buf->buffer + (ring_buf->in & (ring_buf->size - 1)), buffer, len);
/* then put the rest (if any) at the beginning of the buffer */
memcpy(ring_buf->buffer, buffer + len, size - len);
ring_buf->in += size;
return size;
}
#endif
write.c:
#include <sys/ipc.h>
#include <sys/shm.h>
#include "ring.h"
#define KEY_ID 8888
#define N 10
struct sys_data
{
int id;
char name[32];
};
struct sys_data infos[N] = {
{1,"name1"},
{2,"name2"},
{3,"name3"},
{4,"name4"},
{5,"name5"},
{6,"name6"},
{7,"name7"},
{8,"name8"},
{9,"name9"},
{10,"name10"},
};
int main(int argc, char const *argv[])
{
void* shm = NULL;
int shmid = 0;
struct sys_data *data = NULL;
struct ring_buffer* head = NULL;
shmid = shmget((key_t)KEY_ID,SHM_SIZE,0666|IPC_CREAT);
if(shmid==-1)
{
printf("shmget ring fail,%s\n",strerror(errno));
exit(-1);
}else{
printf("shmget ring success shmid=%d\n", shmid);
}
shm = shmat(shmid,(void*)0,0);
if(shm)
{
printf("shmat ring success\n");
}else{
printf("shmat ring error,%s\n",strerror(errno));
exit(-1);
}
head = ring_buffer_init(shm,RING_BUFFER_SIZE);
if(!head){
return 0;
}
int i = 0;
for(i;i < N;i++){
ring_buffer_put(head,&infos[i],sizeof(struct sys_data));
}
printf("\n\n输入一个字符结束\n");
getchar();
struct sys_data tmp;
for(i = 0;i < N; i++){
memset(&tmp,0,sizeof(tmp));
ring_buffer_get(head,&tmp,sizeof(struct sys_data));
printf("tmp.id:%d,tmp.name:%s\n",tmp.id,tmp.name);
}
if(shmctl(shmid,IPC_RMID,NULL) < 0){
printf("shmctl ring fail:%s\n",strerror(errno));
} else{
printf("shmctl ring success\n");
}
return 0;
}
read.c
#include <sys/ipc.h>
#include <sys/shm.h>
#include "ring.h"
#define KEY_ID 8888
#define N 10
struct sys_data
{
int id;
char name[32];
};
int main(int argc, char const *argv[])
{
void* shm = NULL;
int shmid = 0;
struct sys_data *data = NULL;
struct ring_buffer *head = NULL;
shmid = shmget((key_t)KEY_ID,SHM_SIZE,0666|IPC_CREAT);
if(shmid==-1)
{
printf("shmget ring fail,%s\n",strerror(errno));
exit(-1);
}else{
printf("shmget ring success shmid=%d\n", shmid);
}
shm = shmat(shmid,(void*)0,0);
if(shm)
{
printf("shmat ring success\n");
}else{
printf("shmat ring error,%s\n",strerror(errno));
exit(-1);
}
head =(struct ring_buffer *)shm;
printf("head->in:%d head->out:%d head->size:%d\n",head->in,head->out,head->size);
struct sys_data tmp;
int i = 0;
for(i = 0;i < N; i++){
memset(&tmp,0,sizeof(tmp));
printf("come here,line:%d\n",__LINE__);
ring_buffer_get(head,&tmp,sizeof(struct sys_data));
printf("tmp.id:%d,tmp.name:%s\n",tmp.id,tmp.name);
}
return 0;
}
目前我还是找不到哪里出了问题,请大家指点!!!