生产者消费者模型
实现一个先进先出的共享内存shmfifo
使用消息队列即可实现消息的先进先出(FIFO), 但是使用共享内存实现消息的先进先出则更加快速
shmfifo说明:
将申请到的共享内存作为一块缓冲区, 将该内存的首部(p_shm到p_payload的内容)格式化为如上图所示的形式;读/写进程不断的按照先进先出的原则从其中读出/写入数据, 则读/写进程就可以看成生产者/消费者了, 因此,使用信号量sem_mutex(初值为1)来互斥访问共享内存, 使用sem_full(初值为共享缓冲区块数), sem_empty(初值为0)来同步两个进程.。程序中利用到前面写过的一些信号量函数,可以参考前面的。
//结构体类型定义
typedef struct shmhead shmhead_t;
typedef struct shmfifo shmfifo_t;
//共享内存首部定义
struct shmhead
{
unsigned int blksize; //块大小
unsigned int blocks; //总块数
unsigned int rd_index; //读索引块
unsigned int wr_index; //写索引块
};
//整个shmfifo句柄
struct shmfifo
{
shmhead_t *p_shm; //共享内存头部指针
char *p_payload; //有效负载其实地址
int shmid; //共享内存ID
int sem_mutex; //互斥信号量
int sem_full; //满信号量
int sem_empty; //空信号量
};
/**shmfifo初始化
既包含了共享内存的初始化, 也包含了三个信号量的初始化;
小技巧: 对一个IPC对象首先尝试打开, 如果打开失败, 则表示该IPC对象尚未创建, 则需要创建之, 而如果打开成功的话, 则进行其他操作
**/
shmfifo_t *shmfifo_init(int key, int blksize, int blocks)
{
shmfifo_t *fifo = (shmfifo_t *)malloc(sizeof(shmfifo_t));
assert(fifo != NULL);
memset(fifo, 0, sizeof(shmfifo_t));
// 尝试打开共享内存
int shmid = shmget(key, 0, 0);
// 如果打开失败, 则表示该共享内存尚未创建, 则创建
if (shmid == -1)
{
/** 设置共享内存 **/
int size = blksize*blocks + sizeof(shmhead_t); //一个头指针+其他块的长度
//创建共享内存
fifo->shmid = shmget(key, size, IPC_CREAT|0666);
if (fifo->shmid == -1)
err_exit("shmget error");
//创建共享内存成功, 则需要将其连接到进程的地址空间
//void *shmat(int shmid, const void *shmaddr, int shmflg);
fifo->p_shm = (shmhead_t *)shmat(fifo->shmid, NULL, 0);
if (fifo->p_shm == (void *) -1)
err_exit("shmat error");
//将共享内存的首部初始化为struct shmhead
fifo->p_shm->blksize = blksize;
fifo->p_shm->blocks = blocks;
fifo->p_shm->rd_index = 0;
fifo->p_shm->wr_index = 0;
fifo->p_payload = (char *)(fifo->p_shm+1);
/** 设置三个信号量 **/
fifo->sem_mutex = sem_create(key);
sem_setval(fifo->sem_mutex, 1);
fifo->sem_full = sem_create(key+1);
sem_setval(fifo->sem_full, 10);
fifo->sem_empty = sem_create(key+2);
sem_setval(fifo->sem_empty, 0);
}
else
{
fifo->shmid = shmid;
//共享内存已经存在, 并且打开成功, 则需要将其连接到进程的地址空间
fifo->p_shm = (shmhead_t *)shmat(fifo->shmid, NULL, 0);
if (fifo->p_shm == (void *) -1)
err_exit("shmat error");
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);
}
return fifo;
}
/**shmfifo的销毁
既要销毁共享内存, 也要销毁三个信号量, 还需要将malloc出来的shmfifo_t结构体释放掉
**/
void shmfifo_destroy(shmfifo_t *fifo)
{
//释放三个信号量
sem_delete(fifo->sem_mutex);
sem_delete(fifo->sem_full);
sem_delete(fifo->sem_empty);
//分离内存
shmdt(fifo->p_shm);
//删除共享内存
if (shmctl(fifo->shmid, IPC_RMID, NULL) == -1)
err_exit("remove share memory error");
//将fifo内存释放
free(fifo);
}
/**将buf内容按照顺序写入共享内存
注意此处的P,V操作并没有使用SEM_UNDO标记
**/
void shmfifo_put(shmfifo_t *fifo, const void *buf)
{
sem_P(fifo->sem_full);
sem_P(fifo->sem_mutex);
//从结构体中获取写入位置
char *index = fifo->p_payload +
(fifo->p_shm->wr_index * fifo->p_shm->blksize);
memcpy(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);
}
/**将共享内存中的内容按照顺序读出到buf
注意此处的P,V操作并没有使用SEM_UNDO标记
**/
void shmfifo_get(shmfifo_t *fifo, void *buf)
{
sem_P(fifo->sem_empty);
sem_P(fifo->sem_mutex);
//从结构体中获取读出位置
char *index = fifo->p_payload +
(fifo->p_shm->rd_index * fifo->p_shm->blksize);
memcpy(buf, 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);
}
/**测试代码: write.cpp**/
struct Student
{
char name[32];
int age;
};
int main()
{
shmfifo_t *fifo = shmfifo_init(1234, sizeof(Student), 15);
Student s;
bzero(&s, sizeof(s));
strcpy(s.name, "AAAAAAAAAAAAAA");
for (int i = 0; i < 15; ++i)
{
sprintf(&(s.name[8]), "%d", i);
s.age = i;
shmfifo_put(fifo, &s);
cout << "put success" << endl;
}
}
/**测试代码: read.cpp**/
int main()
{
shmfifo_t *fifo = shmfifo_init(1234, sizeof(Student), 3);
Student s;
for (int i = 0; i < 5; ++i)
{
bzero(&s, sizeof(s));
shmfifo_get(fifo, &s);
printf("name: %s, age = %d\n", s.name, s.age);
}
return 0;
}
/**测试代码: 销毁所创建的共享内存与信号量, free**/
int main()
{
shmfifo_t *fifo = shmfifo_init(1234, sizeof(Student), 3);
shmfifo_destroy(fifo);
return 0;
}