最近項目中需要用到共享內存通信,爲了方便操作,加入了一種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;
}
目前我還是找不到哪裏出了問題,請大家指點!!!