以队列的形式使用共享内存

来源:互联网 发布:骨朵网络剧 编辑:程序博客网 时间: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函数即可

0 0