共有メモリ:前回の記事では、shm共有メモリとシステムvセマフォのメカニズムをすでに紹介しているため、ここでは繰り返しません。
アプリケーションシナリオ:
プロセス間通信には多くの方法があり、最も速い方法は共有メモリである必要があります。通信は物理メモリ共有を介して直接実行されるため、速度は他のネットワーク方法と比較できません。ただし、このメモリを管理するための同期メカニズムがないと、アプリケーションのシナリオは非常に制限されます。次に、システムvセマフォを介して共有メモリに同期メカニズムを追加し、完全なプロセス間通信機能にします。
プロセス間の共有リングキューモデル-プロデューサー、コンシューマーモデル:
リングバッファデータ図:
キーコードは次のとおりです:
struct sembuf{
unsigned short sem_num; /* semaphore number */除非使用一组信号量,否则它为0
short sem_op; /* semaphore operation */ p -1, v 1
short sem_flg; /* operation flags */ 填 0就好 SEM_NOWAIT SEM_UNDO
}
typedef struct shmhead {
int rd_idx; // 读入数据索引
int wr_idx; // 写数据索引
int blocks; // 存数据块数量
int blksz; // 每个数据块大小
}shmhead_t;
typedef struct shmfifo {
shmhead_t *p_head; // 共享内存的起始地址
char * p_payload; // 有效数据的起始地址
int shmid; // 打开的共享内存id
int sem_mutex; // 互斥量
int sem_empty; // 还剩多少个可以消费
int sem_full; // 剩余多少个地方可以生产
}shmfifo_t;
主要通过三个信号量来解决进程间读写环形缓冲区的临界区资源的互斥性
// 初始化函数
shmfifo_t *shmfifo_init(key_t key, int blocks, int blksz);
// 放入数据
void shmfifo_put(shmfifo_t *fifo, const void *buf);
// 取得数据
void shmfifo_get(shmfifo_t *fifo, void *buf);
// 结构销毁
void shmfifo_destroy(shmfifo_t *fifo);
#include "shmfifo.h"
typedef union semun{
int val;
}semun;
// 初始化
shmfifo_t* shmfifo_init(key_t key, int blocks, int blksz)
{
shmfifo_t *p = malloc(sizeof(shmfifo_t));
int shmid = shmget(key, 0, 0);
int len = sizeof(shmhead_t) + blocks*blksz; //共享内存段大小
if(shmid == -1 ) // 内存段不存在,创建
{
shmid = shmget(key, len, IPC_CREAT|0644);
if ( shmid == -1)
{
perror("shmget"),
exit(1); //初始化内存段头
}
p->p_head = shmat(shmid, NULL, 0); //将开出的内存段挂载到进程地址空间
p->p_head->rd_idx = 0;
p->p_head->wr_idx = 0;
p->p_head->blocks = blocks;
p->p_head->blksz = blksz; //初始化后段
p->p_payload = (char*)(p->p_head+1);
p->shmid = shmid;
p->sem_mutex = semget(key, 1, IPC_CREAT|0644);
p->sem_empty = semget(key+1, 1, IPC_CREAT|0644);
p->sem_full = semget(key+2, 1, IPC_CREAT|0644);
semun su = {
1}; //设置互斥信号量初值为1
semctl(p->sem_mutex, 0, SETVAL, su);
su.val = blocks;
semctl(p->sem_empty, 0, SETVAL, su);
su.val = 0; //初始不能消费
semctl(p->sem_full, 0, SETVAL, su);
}
else //内存段存在 ,打开
{
p->p_head = shmat(shmid, NULL, 0);
p->p_payload = (char*)(p->p_head+1);
p->shmid = shmid;
p->sem_mutex = semget(key, 0, 0); //
p->sem_empty = semget(key+1, 0, 0);
p->sem_full = semget(key+2, 0, 0);
}
return p;
}
static void P(int id)
{
struct sembuf sb[1] = {
0,-1, 0};
semop(id, sb, 1);
}
static void V(int id)
{
struct sembuf sb[1] = {
0, 1, 0};
semop(id, sb, 1);
}
// 放入数据
void shmfifo_put(shmfifo_t *fifo, const void *buf)
{
P(fifo->sem_empty); //有多少地方可供生产,确保有空位生产
P(fifo->sem_mutex); //保证进程互斥
memcpy(fifo->p_payload + fifo->p_head->wr_idx * fifo->p_head->blksz,uf,fifo->p_head->blksz); //每次写入一个数据块大小
fifo->p_head->wr_idx = (fifo->p_head->wr_idx+1) %fifo->p_head->blocks; //取模,保证数据存满时,转从payload处写数据
V(fifo->sem_full);
V(fifo->sem_mutex);
}
// 取得数据
void shmfifo_get(shmfifo_t* pFifo, void *buf)
{
P(pFifo->sem_full); //确保有数据可取
P(pFifo->sem_mutex); //从内存段读取,拷入buf中
memcpy(buf,pFifo->p_payload + pFifo->p_head->rd_idx* pFifo->p_head->blksz, pFifo->p_head->blksz);
pFifo->p_head->rd_idx = (pFifo->p_head->rd_idx+1) %pFifo->p_head->blocks; //取模,保证数据存满时,转从payload处取数据
V(pFifo->sem_empty);
V(pFifo->sem_mutex);
}
// 销毁
void shmfifo_destroy(shmfifo_t* pFifo)
{
shmdt(pFifo->p_head); //取消内存段挂载
shmctl(pFifo->shmid, IPC_RMID, 0); //释放掉该内存段
//删除信号量
semctl(pFifo->sem_mutex, 0, IPC_RMID, 0);
semctl(pFifo->sem_empty, 0, IPC_RMID, 0);
semctl(pFifo->sem_full, 0, IPC_RMID, 0);
free(pFifo);
}
消费者进程代码:
#include "shmfifo.h" #include <unistd.h>
typedef struct Products{
int id;
char pro_name[10];
}Pro;
int main(){
shmfifo_t* fifo = shmfifo_init(12345, 3, sizeof(Pro));
Pro p;
while( 1){
memset(&p, 0x00, sizeof(p));
shmfifo_get(fifo, &p);
printf("id:%d, 产品名:%s\n", p.id, p.pro_name);
sleep(1);
}
shmfifo_destroy(fifo);
}
生产者进程代码:
#include "shmfifo.h" typedef struct Product{
int id;
char pro_name[10];
}Pro;
int main(){
shmfifo_t *fifo = shmfifo_init(12345, 4, sizeof(Pro));
Pro p;
for (int i=0; i<20; ++i){
memset(&p, 0x00, sizeof(p));
sprintf(p.pro_name, "iphone%d", i);
p.id = i+1;
shmfifo_put(fifo, &p);
printf("put %d ok\n", i);
}
}