一.共享内存通信的实现原理
其实进程之间通信的本质就是让通信双方看见同一份内存。这样当这份内存的数据发生改变的时候,双方进程都能直接观察到结果。这份内存存放在共享数据区,由操作系统提供并维护。共享内存的特性是只会储存进程之间通信产生的临时变量,而不会刷新到磁盘。不过这里有个问题就是,两个进程如何保证找到的共享内存是同一块内存呢。进程之通过key值实现这一点的,key值是由系统路径和你指定的一个id计算而成,当你提供相同的路径和id的时候算出的key值是完全一样的。而共享内存的申请需要key值参与,这样进程就可以拿着key值去找key值相符的共享内存了。注意共享内存不仅仅指的是内存数据块,他同时还包括共享内存的内核数据结构。
二.如何使用共享内存
(1).创建共享内存:创建共享内存有两步,首先计算出key值,在通过key值申请出共享内存
计算key值:使用ftok这个函数
pathname: 系统路径
shmaddr:指定连接的地址
proj_id:指定id
返回值:成功返回一个key值,失败返回-1
申请共享内存:使用shmget这个函数
key:这个共享内存段名字(上文的key值)
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的,这里有两个标志码,IPC_CREAT和IPC_EXECL,前者的作用是创建共享内存,若当前key值的共享内存已经存在,就使用旧的共享内存,后者必须和前者一起组合使用,组合效果确保了每次申请的共享内存都是全新的。
返回值:成功返回一个非负整数,即该共享内存段的标识码(shmid);失败返回-1
(2)挂接共享内存:刚刚创建出来的共享内存是无法直接使用的。因为还没有和当前进程建立起联系。shmat是挂接进程,shmdt是解除挂接
shmat:
shmid: 共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1
shmdt:
int shmdt(const void *shmaddr);
参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段
(3)到这步就可以正常使用共享内存了,我们已经通过shmat拿到了共享内存的指针,我们可以把这一块内存当成一个大字符串来使用。
(4)接触挂接并删除共享内存:
上文当中已经说过解除挂接的函数,删除共享内存
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1
使用时注意一点,共享内存的生命周期不由进程决定,也就是说除非你手动删除,否则一旦创建,一直存在,除非你重启。
三.案例演示
模拟客户输入服务器输出
下图为服务器端
下图为客户端
二.共享内存通信的优势和缺点
优势:使用共享内存的通信方式是最快的。因为它本身是内存级别的读和写。并且没有过多的拷贝。通常我们使用管道通信至少要经历四次拷贝,第一次拷贝到用户输入缓冲区,第二次拷贝到管道文件,第三次拷贝到输出缓冲区,第四次拷贝出来打印。极大的降低了效率。
劣势:共享内存因为直接能看到,没有进程控制,当遭遇多个执行流互相运行访问临界资源的时候可能会造成干扰。可以通过手动添加进程控制保证原子性解决