您的位置:首页 > 运维架构 > Linux

Linux共享内存及共享内存实现互斥循环队列shmFIFO

2018-04-09 00:44 381 查看
Linux进程间通讯的方法有信号,消息队列,管道,共享内存。本篇介绍共享内存。
每一个进程都有自己的虚拟地址空间,每个进程的空间之间严格区分,互不干扰。但通过系统调用开辟一片内存,让不同进程都连接到此空间,就可以实现多进程共享一片空间。
进程本身认为,自己只操作了自己的空间,事实上操作的是一片与其它进程共用的空间。这样可以实现进程间的数据"传输",即进程间通讯。
共享内存是一种很快的进程间通讯方法。
拿共享内存和管道作比较:
 写数据读数据
共享内存直接写入内存直接读取内存
管道调用系统调用写入数据调用系统调用读取数据
可以看到,共享内存在建立内存映射后,对数据的读写都不需要系统调用。而管道读写都需要系统调用。在Linux系统编程(一) 认识系统调用中介绍过,每一次系统调用,都要保护现场数据,转入核心空间操作,结束后恢复现场工作,转入用户空间。管道完成一次完整的读取需要转换四个操作空间,明显效率远低于共享内存。使用方法
头文件:sys/types.h sys/ipc.h sys/shm.h
调用:shmget() shmctl() shmat() shmdt
1.
int shmget(key,size,shmflg)
key_t key ;
int size,shmflg;
第一个参数key是一个整数,可以理解为共享内存的名字,由程序员自己设定,或者可以通过ftok()产生(例如ftok("."'a')),第二个参数一个整数,表示开辟地址空间的大小,单位为字节。第三个参数用来建立共享内存并设定其权限。
用法一:创建共享内存 int id=shmget(key,size,IPC_CREAT|0644) 这里的size应为希望开辟的大小。id接受返回值,为一个识别码。
用法二:获取共享内存id  int id=shmget(key,0,0) 成功将返回名为key的共享内存的识别码。失败返回-1;
2.
char *shmat(shmid,ptr,shmflg)
int shmid;
char *ptr;
int shmflg;
通过此调用,可以将识别码为shmid(shmid必须由shmget获取)的共享内存地址赋给ptr,shmflg用来控制是否分页,以及读写权。可以不做处理,一般情况只需要传0。ptr指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置。返回值为共享内存地址
3.
int shmdt(ptr)
char * ptr;
脱离已经连接上的共享内存。ptr为指向共享内存头的指针。在进程不再使用共享内存的时候应进行次操作。成功返回 0 ,失败返回 -1;
4.
int shmctl(shmid,cmd,buf)
int shmid,cmd;
struct shmid * buf;
这个调用有三个用处:将现有共享内存的状态拷贝一份存至buf指向的结构体中。设定共享内存用户识别码、使用群识别码及共享内存的存取权限。释放共享内存。
为实现这三个功能,将cmd设置为对应的IPC_STAT      IPC_SET       IPC_RMIDshmFIFO只是简单的打开,连接,使用管道,并不能完成正常的进程间通讯。试想,共享内存是不是很类似于多个进程同时对同一个文件进行操作。学习过文件锁的程序员都知道,如果任由进程同时读写文件,将会造成信息丢失,写入错误,一个进程的写入结果覆盖其他进程写入结果等情况,这个时候就要使用文件锁。类似的,要对共享内存的读写加锁。使用信号量,PV操作。举例:shmFIFO


使用如图的数据结构,将共享内存管理为循环队列。设置三个信号量,管理共享内存的操作互斥,读取阻塞。思路:1.开辟制定大小的共享内存,在头部储存空间的 节点大小,节点个数,写索引,读索引。用一个结构体记录:共享内存起始地址,有效负载起始地址,ID以及上述三个信号量。2.设定信号量初始值,mutex=1,full=节点个数,empty=0;3.每次写入前进行p(full),p(mutex)操作,在(有效负载+写索引*节点大小)位置写入数据,将写索引向后偏移一位,写入成功后进行v(empty),v(mutex)操作。4.每次读取前进行p(empty),p(mutex)操作,在(有效负载+读索引*节点大小)位置写入数据,将读索引向后偏移一位,读取成功后进行v(full),v(mutex)操作。    代码:头文件:#ifndef __SHMFIFO_H__
#define __SHMFIFO_H__

#include <sys/ipc.h>

typedef struct shmhead {
unsigned int blocks; // 总块数
unsigned int blksz; // 每块大小
unsigned int rd_idx; // 读索引
unsigned int wr_idx; // 写索引
}shmhead_t;

typedef struct shmfifo {
shmhead_t *p_head; // 共享内存头地址
char * p_payload; // 有效数据起始地址
int shmid; // 共享内存id
int sem_mutex; // 互斥
int sem_full; // 还有多少地方可以装东西
int sem_empty; // 还有多少可以消费
}shmfifo_t;

shmfifo_t *shmfifo_init(key_t key, int blksize, int blocks);
void shmfifo_put(shmfifo_t *fifo,const void *buf);
void shmfifo_get(shmfifo_t *fifo, void *buf);
void shmfifo_destroy(shmfifo_t *fifo);

#endif //__SHMFIFO_H__

实现:#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "shmfifo.h"

union semun { int val; };

shmfifo_t *shmfifo_init(key_t key, int blksize, int blocks)
{
shmfifo_t *p =(shmfifo_t*)malloc(sizeof(shmfifo_t));
memset(p, 0x00, sizeof(shmfifo_t));
int shmid = shmget(key, 0, 0);
int len = blksize*blocks + sizeof(shmhead_t);
if ( shmid == -1 ) { // 不存在,创建
shmid = shmget(key, len, IPC_CREAT|0644);
if ( shmid == -1 )
perror("shmget"),exit(1);
p->shmid = shmid;
p->p_head = (shmhead_t*)shmat(shmid, NULL, 0);
p->p_head->wr_idx = 0;
p->p_head->rd_idx = 0;
p->p_head->blksz = blksize;
p->p_head->blocks = blocks;
p->p_payload=(char*)p->p_head+sizeof(shmhead_t);
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;
su.val = 0;
semctl(p->sem_empty, 0, SETVAL, su);
su.val = blocks;
semctl(p->sem_full, 0, SETVAL, su);
su.val = 1;
semctl(p->sem_mutex, 0, SETVAL, su);
} else { // 存在
p->shmid = shmid;
p->sem_empty = semget(key, 0, 0);
p->sem_full = semget(key+1, 0, 0);
p->sem_mutex = semget(key+2, 0, 0);
p->p_head = (shmhead_t*)shmat(p->shmid, NULL, 0);
p->p_payload=(char*)p->p_head +sizeof(shmhead_t);
}
return p;
}

void Operator_P(int key)
{
int semid=semget(key,0,0);
struct sembuf buf[]={{0,-1,0}};
semop(semid,buf,1);
}

void Operator_V(int key)
{
int semid=semget(key,0,0);
struct sembuf buf[]={{0,1,0}};
semop(semid,buf,1);
}

void shmfifo_put(shmfifo_t *fifo,const void *buf)
{
9693
int mutex=fifo->sem_mutex;
int empty=fifo->sem_empty;
int full=fifo->sem_full;
Operator_P(full);
Operator_P(mutex);
void *put_place=((char*)fifo->p_payload)+(fifo->p_head->wr_idx)*(fifo->p_head->blksz);
memcpy(put_place,buf,fifo->p_head->blksz);
fifo->p_head->wr_idx=(fifo->p_head->wr_idx+1)%(fifo->p_head->blocks);
Operator_V(empty);
Operator_V(mutex);
}

void shmfifo_get(shmfifo_t *fifo, void *buf)
{
int mutex=fifo->sem_mutex;
int empty=fifo->sem_empty;
int full=fifo->sem_full;
Operator_P(empty);
Operator_P(mutex);
void *get_place=((char*)fifo->p_payload)+(fifo->p_head->rd_idx)*(fifo->p_head->blksz);
memcpy(buf,get_place,fifo->p_head->blksz);
fifo->p_head->rd_idx=(fifo->p_head->rd_idx+1)%fifo->p_head->blocks;
Operator_V(full);
Operator_V(mutex);
}

void shmfifo_destroy(shmfifo_t *fifo)
{
shmdt(fifo->p_head);
free(fifo);
fifo==NULL;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息