共享内存(SHM)
共享内存是效率最高的 IPC,因为他抛弃了内核这个“代理人”,直截了当地将一块裸
露的内存放在需要数据传输的进程面前,让他们自己搞,这样的代价是:这些进程必须小心
谨慎地操作这块裸露的共享内存,做好诸如同步、互斥等工作,毕竟现在没有人帮他们来管
理了,一切都要自己动手。也因为这个原因,共享内存一般不能单独使用,而要配合信号量、
互斥锁等协调机制,让各个进程在高效交换数据的同时,不会发生数据践踏、破坏等意外。
共享内存的思想很朴素,进程与进程之间虚拟内存空间本来相互独立,不能互相访问的,
但是可以通过某些方式,使得相同的一块物理内存多次映射到不同的进程虚拟空间之中,这
样的效果就相当于多个进程的虚拟内存空间部分重叠在一起,看示意图。

使用共享内存的一般步骤是:
1
,获取共享内存对象的
ID
2
,将共享内存映射至本进程虚拟内存空间的某个区域
3
,当不再使用时,解除映射关系
4
,当没有进程再需要这块共享内存时,删除它
信号量(SEM)
信号量 SEM全称 Semaphore,中文也翻译为信号灯。作为system-V
IPC 的最后一种,信号量跟前面的
MSG
和
SHM
有极大的不同,
SEM
不是用来传输数据的, 而是作为“旗语”,用来协调各进程或者线程工作的。
一些基本概念如下:
1
,多个进程或线程有可能同时访问的资源(变量、链表、文件等等)称为共享资源,
也叫临界资源(critical resources)。
2
,访问这些资源的代码称为临界代码,这些代码区域称为临界区(critical zone)。
3
,程序进入临界区之前必须要对资源进行申请,这个动作被称为
P
操作,这就像你要
把车开进停车场之前,先要向保安申请一张停车卡一样,
P
操作就是申请资源,如果申请成
功,资源数将会减少。如果申请失败,要不在门口等,要不走人。
4
,程序离开临界区之后必须要释放相应的资源,这个动作被称为
V
操作,这就像你把
车开出停车场之后,要将停车卡归还给保安一样,
V
操作就是释放资源,释放资源就是让资
源数增加。
所有一起访问共同临界资源的进程都必须遵循以上游戏规则,否则大家就都乱套了,但
是值得注意的是:这些规则是自愿的,如果有进程就是胡来——在访问资源之前不申请,那
么将会可能导致逻辑谬误,就像开车压死保安直接撞进停车场一样,虽然于情于理都不可以,
物理上阻止不了这种行为。
system-V
的信号量非常类似于停车场的卡牌,想象一个有
N
个车位的停车场,每个
车位是立体的可升降的,能停
n
辆车,那么我们可以用一个拥有
N
个信号量元素,每个信
号量元素的初始值等于
n
的信号量来代表这个停车场的车位资源——某位车主要把他的
m
辆车开进停车场,如果需要
1
个车位,那么必须对代表这个车位的信号量元素申请资源,
如果
n
大于等于
m
,则申请成功,否则不能把车开进去。

对 于共享内存和信号量的使用
发送端代码如下jack.c
#include<stdio.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <stdlib.h>
union semun
{
int val; /* 当 cmd 为 SETVAL 时使用 */
struct semid_ds *buf; /* 当 cmd 为 IPC_STAT 或 IPC_SET 时使用 */
unsigned short *array; /* 当 cmd 为 GETALL 或 SETALL 时使用 */
struct seminfo *__buf; /* 当 cmd 为 IPC_INFO 时使用 */
};
char * shm_init()
{
//获取KEY值
int key=ftok("./",'K');
int shm_id=shmget(key, 4096,IPC_CREAT|0666);
if(shm_id==-1)
{
perror("shmget id erorr");
exit(1);//直接结束进程
}
//映射共享内存
char *shm_map=shmat(shm_id, NULL, 0);
if(shm_map==(void *)-1)
{
perror("shm map error");
exit(1);//直接结束进程
}
return shm_map;
}
int sem_init()
{
//获取新的KEY值
int key=ftok("./",'X');
//获取信号量的ID
int sem_id=semget(key, 2, IPC_CREAT|0644);
if(sem_id==-1)
{
perror("sem get id error ");
exit(1);
}
//初始化信号量的内容 主要是初始化他们的初始化的资源数
//初始化没有数据,有一个空间
union semun set;
set.val=0;
semctl(sem_id, 0, SETVAL, set); //0 初始化数据资源
set.val=1;
semctl(sem_id, 1, SETVAL,set ); //1 初始化空间资源
return sem_id;
}
int main(int argc, char const *argv[])
{
//配置共享内存并初始化
char *shm_map=shm_init();
//初始化信号量
int sem_id=sem_init();
//再写入共享内存之前需要现申请一个空间资源
/* struct sembuf
{
unsigned short sem_num; /* 信号量元素序号(数组下标)
short sem_op; /* 操作参数 *
short sem_flg; /* 操作选项
};*/
struct sembuf space={ //设置空间资源
.sem_num=1, //需要设置的空间资源元素下表为1
.sem_flg=0, //设置标记为0 啥也不选
.sem_op=-1 //-1表示资源量即将建议 申请资源
};
struct sembuf data={ //设置空间资源
.sem_num=0, //需要设置的空间资源元素下表为0
.sem_flg=0, //设置标记为0 啥也不选
.sem_op=1 //1表示资源量即将加1 释放资源
};
while (1)
{
//等待空间资源 会阻塞 如果资源暂时不能得到则会阻塞等待
semop(sem_id, &space, 1);
printf("请输入需要发送的数据\n");
fgets(shm_map,4096,stdin);
//设置数据资源为1
semop(sem_id, &data, 1);/* code */
}
return 0;
}
接收端代码 rocs.c
#include<stdio.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <stdlib.h>
union semun
{
int val; /* 当 cmd 为 SETVAL 时使用 */
struct semid_ds *buf; /* 当 cmd 为 IPC_STAT 或 IPC_SET 时使用 */
unsigned short *array; /* 当 cmd 为 GETALL 或 SETALL 时使用 */
struct seminfo *__buf; /* 当 cmd 为 IPC_INFO 时使用 */
};
char * shm_init()
{
//获取KEY值
int key=ftok("./",'K');
int shm_id=shmget(key, 4096,IPC_CREAT|0666);
if(shm_id==-1)
{
perror("shmget id erorr");
exit(1);//直接结束进程
}
//映射共享内存
char *shm_map=shmat(shm_id, NULL, 0);
if(shm_map==(void *)-1)
{
perror("shm map error");
exit(1);//直接结束进程
}
return shm_map;
}
int sem_init()
{
//获取新的KEY值
int key=ftok("./",'X');
//获取信号量的ID
int sem_id=semget(key, 2, IPC_CREAT|0644);
if(sem_id==-1)
{
perror("sem get id error ");
exit(1);
}
//初始化信号量的内容 主要是初始化他们的初始化的资源数
//初始化没有数据,有一个空间
union semun set;
set.val=0;
semctl(sem_id, 0, SETVAL, set); //0 初始化数据资源
set.val=1;
semctl(sem_id, 1, SETVAL,set ); //1 初始化空间资源
return sem_id;
}
int main(int argc, char const *argv[])
{
//配置共享内存并初始化
char *shm_map=shm_init();
//初始化信号量
int sem_id=sem_init();
//再写入共享内存之前需要现申请一个空间资源
/* struct sembuf
{
unsigned short sem_num; /* 信号量元素序号(数组下标)
short sem_op; /* 操作参数 *
short sem_flg; /* 操作选项
};*/
struct sembuf space={ //设置空间资源
.sem_num=1, //需要设置的空间资源元素下表为1
.sem_flg=0, //设置标记为0 啥也不选
.sem_op=1 //1表示资源量即将加以 释放资源
};
struct sembuf data={ //设置空间资源
.sem_num=0, //需要设置的空间资源元素下表为0
.sem_flg=0, //设置标记为0 啥也不选
.sem_op=-1 //1表示资源量即将加1 释放资源
};
while (1)
{
printf("wait 数据\n");
//等待数据资源 会阻塞 如果资源暂时不能得到则会阻塞等待
semop(sem_id, &data, 1);
printf("Jacl 说:%s\n",shm_map);
//设置空间资源为1
semop(sem_id, &space, 1);/* code */
}
return 0;
}