共享内存区是最快的IPC(进程内通信)形式,不再通过执行进入内核的系统调用来传递彼此的数据
1.共享内存的原理
IPC通信的本质是让不同的进程先看到同一份资源,然后再进行通信,所以想要通过共享内存进行通信,那么第一步一定是让两个进程能够看到看到物理内存中的同一份资源。
2.共享内存函数
通过共享内存函数来创建共享内存
shmget:
功能:
创建共享内存原型:
int shmget(key_t key, size_t size, int shmflg);
参数:
key:该共享内存段名字
size:共享内存的大小
shmflg:由九个权限标志位构成
若取值为IPC_CREAT:共享内存不存在,创建并返回;
共享内存存在,获取并返回;
若取值为IPC_CREAT | IPC_EXCL:共享内存不存在,创建并返回
共享内存已存在,出错返回
返回值:
成功返回一个非负整数,即该共享内存的标识码(shmid)
失败返回-1
注:所以当想要创建一个新的共享内存时,shmflg的取值应为:IPC_CREAT | IPC_EXCL
问:这个key值从何而来?以及这个key值有和作用?
答1:key值是通过ftok函数得来的,ftok会根据文件名/路径相应的inode以及proj_id生成一个唯一的key值,且该key值和路径(pathname)是一一对应的关系
ftok:
功能:
用来生成一个唯一的key值,该key值用于IPC 通信,共享内存的位置由系统决定
原型:
key_t ftok(const char *pathname, int proj_id);
参数:
pathname:文件路径,必须存在
proj_id:项目标识符,通常是一个字符(0~255之间的整数)
注:为什么是路径?除了历史原因之外,路径具有唯一性。
答2:显然这个key值是用来创建共享内存的。具体怎么实现的呢?比如当前进程需要创建一个新的共享内存,那么在shmget中,只要key值相同,他们就能够打开同一个共享内存,此时就满足了不同进程看到同一份资源的要求。
进程A和进程B的虚拟地址不同,但是它们指向了同一块物理内存
除了让不同进程能够看到同一份资源外,我们还需要将物理内存中的共享内存和进程的虚拟地址建立映射关系
shmat:
功能:
将共享内存段连接到进程地址空间(建立物理内存和虚拟内存间的映射关系)
原型:
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
shmid:创建/打开共享内存成功时返回给当前进程的共享内存标识码
shmaddr:指定连接的地址
shmflg:一般为nulltpr
返回值:
成功返回一个指针,指向共享内存的第一个节
失败返回-1
当前进程不在使用共享内存时,需要与共享内存脱离
shmdt:
功能:
将共享内存段与当前进程脱离
原型:
int shmdt(const void *shmaddr);
参数:
shmaddr: 由shmat所返回的指针
返回值:
成功返回0
失败返回-1
注:脱离不等于删除当前内存段
shmctl:
功能:
用于控制共享内存
原型:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向⼀个保存着共享内存的模式状态和访问权限的数据结构
返回值:
成功返回0
失败返回-1
注:三个可取值
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET:在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值
IPC_RMID:删除共享内存段
3.初步认识信号量
对于共享内存而言,它没有保护机制(当一个用户的信息还没有写完时,可能就被另一个用户读走了),没有保护机制就会造成读取数据的不一致。因此我们需要引入信号量来解决这个问题
3.1概念补充
在初步认识信号量前,需要对某些概念进行补充。
①:代码分为临界区和非临界区
对于非临界区而言,即每个程序自己的代码,他们之间不会相互影响
对于临界区而言,涉及资源互斥的部分,也就上面提到的共享资源访问时出现问题的部分
②:互斥
只允许一个程序访问进程
③:临界资源
被保护起来的资源
④:同步
多个进程访问临界资源时,具有一定的顺序性
⑤:原子性
简单说就是做和不做的区别
注:对共享资源的保护,总之就是对访问共享资源的代码进行保护
3.2信号量
举一个电影院的例子:
一个影厅只有100个位置,如果票重复和提供的座位不足以发售的票都会出现问题。那么解决问题的措施就是 1.避免出现座位重复的票 2.避免发售的票太多。因此进入影院前,需要订票来确定自己座位对于共享内存而言,将共享内存分块访问,那么所做的就是 1.避免多个进程访问共享内存中的同一块资源 2.避免过多进程对该共享内存进行访问
因此信号量的本质是一个计数器,用于描述临界资源中,资源数量的多少,如果进程申请资源成功就做减减操作,直至为零。
注:任何进程想要访问临界资源,必须先申请信号量,本质是对资源(座位)的预订机制
当进程申请信号量时,就做减减操作,保证该操作具有原子性,该操作称为p操作
当进程不用时,就做加加操作,同时保证该操作具有原子性,该操作称为v操作
3.3不同信号量
二元信号量:信号量只有0或者1
多元信号量:1以上就是多元信号量,内部资源可以供多个进程使用