关于System V共享内存和信号量的知识已经学过了,这次主要是综合这两者来解决一个问题达到灵活运用的目的,下面开始:
生产者消费者问题:
关于什么是“生产者消费者”,应该都比较清楚,这里还是先贴上百度百科对它的描述:
下面用图来说明一下该问题:
以上就是生产者消费者从逻辑上的一个解决方案,从中可以看到这是互斥跟同步相结合的例子,下面则用所画的这些模型来实现一下shmfifo
实现shmfifo:
为什么要实现共享内存的先进先出的缓冲区(shmfifo)呢?实际上要实现进程间通信可以直接用消息队列来实现先进先出的队列,但是,由于消息队列还实现了其它的功能,如果仅仅只是想要先进先出这样的一个功能的话,能使用共享内存来实现的话,效率会更高,因为对共享内存的访问不涉及到对内核的操作,这个之前也有讲过,因此就有必要实现一个shmfifo。
要实现这样的一个缓冲区,我们可以做一些假定,假定放到缓冲区当中的数据块是定长的,并且可以有多个进程往缓冲区中写入数据,也有多个进程往缓冲区中读取数据,所以这是典型的生产者消费者问题,这块缓冲区刚才说过可以用共享内存的方式来实现,但是有一个问题需要思考:生产者进程当前应该在什么位置添加产品,消费者进程又从什么位置消费产品呢?所以说还需要维护这些状态,所以很自然地就能想到将这些状态保存在共享内存当中,如下:
由于多个生产者都能往里面添加产品,多个消费者也能够从里面消费产品,那生产者在生产产品的时候应该放在什么位置呢?消费者又该从哪里消费产品呢?下面来说明下:
而这时再次生产就会是在0的位置上开始了:
可见这是一个环形缓冲区,可以重复利用的,基于这些分析下面来看一下所定义出来的数据结构:
有了这些数据结构实际上就能够实现了shmfifo了,下面实现一下:
由于用到了信号量,所以将之前的信号量相关的函数及定义放到一个单独的文件当中,里面代码都是之前学过的,就不多解释
ipc.h:
#ifndef _IPC_H_
#define _IPC_H_
#include #include #include #include #include #include #include #include #include
#define ERR_EXIT(m) \
do\
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
union semun {
int val; /*value for SETVAL */
struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ unsigned short *array; /*array for GETALL, SETALL */
/* Linux specific part: */
struct seminfo *__buf; /*buffer for IPC_INFO */ };
int sem_create(key_t key);
int sem_open(key_t key);
int sem_p(int semid);
int sem_v(int semid);
int sem_d(int semid);
int sem_setval(int semid, int val);
int sem_getval(int semid);
int sem_getmode(int semid);
int sem_setmode(int semid,char* mode);
#endif /* _IPC_H_ */
以上文件是为了实现shmfifo提供辅助功能的,下面则开始实现它,分头文件及具体实现:
shmfifo.h:
#ifndef _SHM_FIFO_H_
#define _SHM_FIFO_H_
#include "ipc.h"
typedef structshmfifo shmfifo_t;
typedef structshmhead shmhead_t;
structshmhead
{
unsigned int blksize; //块大小
unsigned int blocks; // 总块数
unsigned int rd_index; //读索引
unsigned int wr_index; //写索引
};
structshmfifo
{
shmhead_t *p_shm; // 共享内存头部指针
char *p_payload; //有效负载的起始地址
int shmid; // 共享内存ID
int sem_mutex; // 用来互斥用的信号量
int sem_full; //用来控制共享内存是否满的信号量
int sem_empty; //用来控制共享内存是否空的信号量
};
shmfifo_t* shmfifo_init(int 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 /* _SHM_FIFO_H_ */
下面来具体解释一下:
下面来具体实现一下些这函数:
这个方法既可以创建共享内存信号量,也可以打开共享内存信号量,所以下面可以做一个判断:
接下来还得初始化共享内存中的其它字段:
接下来对其信号量集中的信号进行初始化:
shmfifo的初始化函数就已经写完了,接下来来实现第二个函数:shmfifo_put(生产产品),对于生产者的过程,上面也说明过,则严格按照该步骤来进行实现:
下面则开始实现,首先先按照流程把代码框架写出来:
那如何生产产品呢?先来看下图:
首先进行数据偏移:
在生产一个产品之后,下一次要生产的位置则要发生改变,所以:
这样生产产品的函数实现就如上,类似的,消费产品实现就容易了,依照这个流程:
具体实现如下:
接下来实现最后一个函数,就是资源释放:
好了,shmfifo.c的所有函数已经实现,先贴出代码:
shmfifo.c:
#include "shmfifo.h" #include
shmfifo_t* shmfifo_init(int key, int blksize, intblocks)
{
//分配内存空间
shmfifo_t *fifo = (shmfifo_t *)malloc(sizeof(shmfifo_t));
assert(fifo != NULL);
memset(fifo, 0, sizeof(shmfifo_t));
intshmid;
shmid = shmget(key, 0, 0);
int size = sizeof(shmhead_t) + blksize*blocks;
if (shmid == -1)
{//创建共享内存
fifo->shmid = shmget(key, size, IPC_CREAT | 0666);
if (fifo->shmid == -1)
ERR_EXIT("shmget");
fifo->p_shm = (shmhead_t*)shmat(fifo->shmid, NULL, 0);
if (fifo->p_shm == (shmhead_t*)-1)
ERR_EXIT("shmat");
fifo->p_payload = (char*)(fifo->p_shm + 1);
fifo->p_sem->blksize = blksize;
fifo->p_shm->blocks = blocks;
fifo->p_shm->rd_index = 0;
fifo->p_shm->wr_index = 0;
fifo->sem_mutex = sem_create(key);
fifo->sem_full = sem_create(key+1);
fifo->sem_empty = sem_create(key+2);
sem_setval(fifo->sem_mutex, 1);
sem_setval(fifo->sem_full, blocks);
sem_setval(fifo->sem_empty, 0);
}
else {//打开共享内存
fifo->shmid = shmid;
fifo->p_shm = (shmhead_t*)shmat(fifo->shmid, NULL, 0);
if (fifo->p_shm == (shmhead_t*)-1)
ERR_EXIT("shmat");
fifo->p_payload = (char*)(fifo->p_shm + 1);
fifo->sem_mutex = sem_open(key);
fifo->sem_full = sem_open(key+1);
fifo->sem_empty = sem_open(key+2);
}
returnfifo;
}
void shmfifo_put(shmfifo_t *fifo, const void *buf)
{
sem_p(fifo->sem_full);
sem_p(fifo->sem_mutex);
//生产产品
memcpy(fifo->p_payload+fifo->p_shm->blksize*fifo->p_shm->wr_index,
buf, fifo->p_shm->blksize);
fifo->p_shm->wr_index = (fifo->p_shm->wr_index + 1) % fifo->p_shm->blocks;
sem_v(fifo->sem_mutex);
sem_v(fifo->sem_empty);
}
void shmfifo_get(shmfifo_t *fifo, void *buf)
{
sem_p(fifo->sem_empty);
sem_p(fifo->sem_mutex);
memcpy(buf, fifo->p_payload+fifo->p_shm->blksize*fifo->p_shm->rd_index,
fifo->p_shm->blksize);
fifo->p_shm->rd_index = (fifo->p_shm->rd_index + 1) % fifo->p_shm->blocks;
sem_v(fifo->sem_mutex);
sem_v(fifo->sem_full);
}
void shmfifo_destroy(shmfifo_t *fifo)
{
//删除创建的信息量集
sem_d(fifo->sem_mutex);
sem_d(fifo->sem_full);
sem_d(fifo->sem_empty);
//删除共享内存
shmdt(fifo->p_shm);//删除共享内存头部
shmctl(fifo->shmid, IPC_RMID, 0);//删除整个共享内存
//释放fifo的内存
free(fifo);
}
下面则写两个测试程序,分别用来生产、消费产品:
原文链接:https://www.cnblogs.com/webor2006/p/4204693.html