以队列的形式使用共享内存
来源:互联网 发布:骨朵网络剧 编辑:程序博客网 时间:2024/05/22 01:26
共享内存允许多个进程使用某一段存储区,数据不需要在进程之间复制,多个进程都把该内存区域映射到自己的虚拟地址空间,直接访问该共享内存区域,从而可以通过该区域进行通信,是一种速度很快IPC。
下面是共享内存映射图
一般描述使用共享内存,都是以普通缓冲区的形式访问,这里除了讲述共享内存的原理和使用方法,还会实现用队列的方式使用共享内存。
创建共享内存
int shmget(key_t key, size_t size, int shmflg)
利用shmget函数创建共享内存
第一个参数key用于表示共享内存的标识符,函数会利用这个标识符生成一个ID,表示创建的共享内存,通常用ftok函数来产生这个key:
key_t ftok( const char * fname, int id )
fname是一个现存的文件,id是子序号(只取低8位,不能为0),ftok方法会通过fname文件所在文件系统的信息,索引节点号,以及id组合成一个ID并且返回
第二个参数表示要创建的共享内存大小
第三个参数是权限标志,一般用IPC_CREAT,若共享内存不存在则创建,如果想要其他选项,可以使用或操作加上
成功调用后shmget会返回表示共享内存的ID,接下来利用这个ID把共享内存映射到进程的地址空间
void *shmat(int shm_id, const void *shm_addr, int shmflg)
第一个参数,shm_id是由shmget函数返回的共享内存标识。
第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为0,表示让系统来选择共享内存的地址。
第三个参数,shm_flg是一组标志位,通常为0
调用成功时返回一个指向共享内存第一个字节的指针
销毁共享内存
当使用完共享内存,要进行销毁操作
int shmdt(const void *shmaddr)
这个函数只是把共享内存到本进程地址空间的映射解除,参数就是调用shmat时的返回值(指向共享内存的指针)
接着才是真正删除共享内存
int shmctl(int shm_id, int command, struct shmid_ds *buf)
第一个参数是共享内存的标识符(即shmget的返回值)
第二个参数是命令,如果要删除共享内存,一般使用IPC_RMID
第三个参数是共享内存管理结构体,删除时一般为0
用队列的方式使用共享内存
实际在项目里使用共享内存,简单的缓冲区形式往往无法满足需求。
比如:要求一个进程不断往共享内存写入数据,而另一个进程从共享内存读出数据,如果一定要读进程读取数据后,写进程才能写入新的数据,在性能上无疑是一个缺陷,如果写进程不理会数据是否被读进程获取而不断地写入新数据,那么原来的数据将被覆盖。
我们很容易想到的是利用队列实现上述要求,下面将讲述如何用队列的方式使用共享内存
首先,我们要定义数据结构:
#define MAX_NODE_DATA_SIZE 65535#define MAX_QUEUE_NODE_COUNT 127typedef struct buffer_node_{ uint16_t dataLen; uint8_t data[MAX_NODE_DATA_SIZE];} buffer_node_t;typedef struct buffer_queue_{ buffer_node_t queue[MAX_QUEUE_NODE_COUNT]; int front; int rear; bool Init(){ front = rear = 0; memset(queue, sizeof(buffer_node_t)*MAX_QUEUE_NODE_COUNT, 0); } bool isEmpty(){ return (rear == front); } bool isFull(){ return ((rear+1)%MAX_QUEUE_NODE_COUNT == front); } bool Enqueue(buffer_node_t *node){ if(isFull()){ return false; } memcpy(&queue[rear], node, sizeof(buffer_node_t)); rear = (rear+1)%MAX_QUEUE_NODE_COUNT; return true; } buffer_node_t* Dequeue(){ if(isEmpty()){ printf("%d\n",__LINE__); return NULL; } buffer_node_t* node = &queue[front]; front = (front+1)%MAX_QUEUE_NODE_COUNT; return node; }}buffer_queue_t;
接下来创建我们需要的共享内存:
buffer_manage_t *bufferManage = NULL;int bufferID = 0;key_t k;bufferID = shmget(k = ftok("./tsm3", 1), sizeof(buffer_manage_t), IPC_CREAT);bufferManage = (buffer_manage_t*)shmat(bufferID, 0, 0);
如果上面创建共享内存这一步成功,基本也就没有大问题了,其实所谓用队列的方式使用共享内存,说白了就是把共享内存转换为我们自己定义的数据结构,这个和malloc其实很像,当利用malloc申请内存时,返回的是void*类型指针,程序员根据需要可以把指针转换成自己需要使用的类型。
我们把申请的共享内存首地址赋值给bufferManage 后,就可以利用结构体里的方法操作队列,这就是把队列的操作方法定义在结构体里的原因了
理解了这点之后,就可以很容易地用队列的方式使用共享内存
#include <sys/ipc.h>#include <sys/shm.h>#include <errno.h>#include <string.h>#include <vector>#include <stdio.h>#include <string.h>#include <malloc.h>#include <unistd.h>using namespace std;#define MAX_NODE_DATA_SIZE 65535#define MAX_QUEUE_NODE_COUNT 32typedef unsigned char uint8_t;typedef unsigned short uint16_t;typedef struct buffer_node_{ uint16_t dataLen; uint8_t data[MAX_NODE_DATA_SIZE];} buffer_node_t;typedef struct buffer_queue_{ buffer_node_t queue[MAX_QUEUE_NODE_COUNT]; int front; int rear; bool Init(){ front = rear = 0; memset(queue, sizeof(buffer_node_t)*MAX_QUEUE_NODE_COUNT, 0); } bool isEmpty(){ return (rear == front); } bool isFull(){ return ((rear+1)%MAX_QUEUE_NODE_COUNT == front); } bool Enqueue(buffer_node_t *node){ if(isFull()){ return false; } memcpy(&queue[rear], node, sizeof(buffer_node_t)); rear = (rear+1)%MAX_QUEUE_NODE_COUNT; return true; } buffer_node_t* Dequeue(){ if(isEmpty()){ return NULL; } buffer_node_t* node = &queue[front]; front = (front+1)%MAX_QUEUE_NODE_COUNT; return node; }}buffer_queue_t;typedef struct buffer_manage_{ buffer_queue_t sendQueue; buffer_queue_t receiveQueue; void buffer_init_(){ sendQueue.Init(); receiveQueue.Init(); }} buffer_manage_t;buffer_manage_t *bufferManage = NULL;int bufferID = 0;bool Create_ShareMem(){ key_t k; printf("size is %d\n",sizeof(buffer_manage_t)); bufferID = shmget(k = ftok("./tsm3", 1), sizeof(buffer_manage_t), IPC_CREAT); printf("k is %d\n",k); if(bufferID == -1){ printf("Create_ShareMem shmget failed errno = %d, error msg is %s\n", errno,strerror(errno)); } printf("Create_ShareMem shmget bufferID = %d, error = %d\n", bufferID, errno); bufferManage = (buffer_manage_t*)shmat(bufferID, 0, 0); printf("Create_ShareMem shmget bufferManage = %p, error = %d\n", bufferManage, errno); if(bufferManage == (void *) -1 || bufferManage == NULL){ bufferManage = NULL; printf("Create_ShareMem shmat failed errno = %d", errno); return false; } bufferManage->buffer_init_(); return true;}bool Delete_ShareMem(){ if((shmdt(bufferManage) != 0)){ printf("Delete_ShareMem shmdt failed errno = %d", errno); } if(shmctl(bufferID, IPC_RMID, 0) != 0){ printf("Delete_ShareMem shmctl failed errno = %d", errno); return false; } return true;}bool ShareMem_AddData(uint8_t* data, uint16_t dataLen){ bool ret = false; buffer_node_t* node; node = (buffer_node_t*)malloc(sizeof(buffer_node_t)); memset(node, 0, sizeof(buffer_node_t)); node->dataLen = dataLen; if(node->dataLen > MAX_NODE_DATA_SIZE) node->dataLen = MAX_NODE_DATA_SIZE; if(data != NULL){ memcpy(node->data, data, node->dataLen); } if(bufferManage != NULL){ bufferManage->receiveQueue.Enqueue(node); ret = true; } return ret;}bool ShareMem_RemoveData(uint8_t** data, uint16_t* dataLen){ bool ret = false; if(bufferManage != NULL){ buffer_node_t* node = NULL; //bufferManage->sendQueueProtected.Lock(); node = bufferManage->sendQueue.Dequeue(); if(node != NULL){ *data = (uint8_t*)malloc(node->dataLen); if(*data != NULL){ memcpy(*data, node->data, node->dataLen); *dataLen = node->dataLen; ret = true; } } //bufferManage->sendQueueProtected.Unlock(); } return ret;}void printData(){ for(int i = bufferManage->receiveQueue.front; i < bufferManage->receiveQueue.rear; i++) { printf("idx is %d,data is %s\n",i,bufferManage->receiveQueue.queue[i].data); } }int main(){ Create_ShareMem(); while(1){ sleep(2); uint8_t fromT1[10] = "123456789"; ShareMem_AddData(1, fromT1, 10); printData(); sleep(1); //ShareMem_RemoveData(); } Delete_ShareMem(); return 0;}
另一个读进程的代码差不多,只要改一下main函数即可
- 以队列的形式使用共享内存
- 嵌入式DTU实际用到的数组队列形式共享内存实现结果
- 嵌入式DTU实际用到的数组队列形式共享内存实现结果
- 循环队列和以单链表的形式实现队列
- 共享内存队列的实现
- linux 共享内存消息队列使用
- 以脚本的形式使用ftp服务器
- 共享内存的使用
- 共享内存的使用
- 共享内存的使用
- 共享内存的使用
- 共享内存的使用
- 共享内存的使用
- PHP共享内存实现的消息队列
- 管道 消息队列 共享内存的优缺点
- 共享内存无锁队列的实现
- 负数为何在内存中以补码的形式存在?
- Linux c 基于内存的进程通信—共享内存、共享队列(消息队列)
- 第十二周项目 阅读程序a
- undefined reference问题总结
- 347. Top K Frequent Elements
- SQ语句
- 最常用的排序——快速排序
- 以队列的形式使用共享内存
- Caffe 问题汇总
- Multi-column deep neural networks for image classification阅读
- python3.x文件IO学习笔记
- SQ语句
- 使用Git远程仓库管理代码
- 单向队列queue的使用
- x86桌面安卓Remix OS新版发布:支持32位电脑
- 腾讯云在线直播二