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

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_RMID

shmFIFO

只是简单的打开,连接,使用管道,并不能完成正常的进程间通讯。

试想,共享内存是不是很类似于多个进程同时对同一个文件进行操作。学习过文件锁的程序员都知道,如果任由进程同时读写文件,将会造成信息丢失,写入错误,一个进程的写入结果覆盖其他进程写入结果等情况,这个时候就要使用文件锁。

类似的,要对共享内存的读写加锁。使用信号量,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)
{
	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;
}

猜你喜欢

转载自blog.csdn.net/qq_15689151/article/details/79859817