linux:进程间通信之共享内存
1.什么是共享内存?
共享内存时两个进程共享同一块内存,两个进程之间的通信不再牵扯到内核,而是直接在用户状态之间直接通信,即就是进程之间的通信不再通过执行内核的系统调用来传递彼此之间的数据,共享内存其实就是一块简单的内存,需要通信的进程可以随意的在它上面进行写读数据
2.共享内存是如何来进行进程间的通信的?
共享内存和管道、消息队列一样,都是两个进程进行通信的桥梁,与管道
和消息队列不同的是共享内存通信时不会涉及到内核,而管道和消息队列都需
要内核的系统调用来实现自己的功能。故共享内存比管道和消息队列的效率高,
而且实现起来简单
共享内存比管道和消息队列的效率高,为什么?
(1)因为管道和消息队列需要和内核打交道;
(2)共享内存没有内置同步互斥机制,因为同步互斥机制会拖慢运行速度,虽然同步互斥机制可以保证数据的完整性。
3.共享内存的特点?
共有5个特点
(1)生命周期随内核;
故共享内存必须手动删除,如果不删除的话,那共享内存就会一直存在于内核,除非操作系统重启。
(2)可用于随意进程;
(3)可实现双向通信;
(4)不存在面向数据报或者面向字节流的特性;
(5)没有内置同步互斥机制;
4.共享内存的优缺点?
优点:
(1)共享内存效率高;
(2)实现起来也比较简单;
(3)支持随机访问;
缺点:
由于共享内存支持随机访问,故会导致通信时涉及到的数据不安全;
5.共享内存相关函数?
(1)shmget()
1.功能:用来创建共享内存;
2.头文件:#include<sys/shm.h>
3.int shmget(key_t key,size_t size,int shmflg);
参数说明:
(1) key:此共享内存的名字
此参数是ftok()函数的返回值
(2)size:创建的共享内存的大小
(3)shmflg:由9个权限标志构成
4.返回值
成功:返回一个非负整数,即该共享内存的标识码
失败:返回-1
(2)shmat()
1.功能:将共享内存段连接到进程地址空间
2.头文件:#include<sys/shm.h>
3.int shmat(int shmid,const void* shmaddr,int shmflg);
参数说明:
(1)shmid:是shmget函数的返回值,是共享内存的标识码
(2)shmaddr:是此共享内存需要连接的地址
shmaddr:为NULL,操作系统会给此共享内存自动选择一个地址
ps:此参数一般设为NULL,让操作系统为我们分配地址
(3)shmflg:此参数的两个可能取值是SHM_RND,SHM_RDONLY
ps:此参数一般设为0
4.返回值
成功:返回一个指针,指向共享内存第一个字节
失败:返回-1
(3)shmdt()
1.功能:将共享内存段与当前进程脱离
2.头文件:#include<sys/shm.h>
3.int shmdt(int shmid,void* shmaddr)
参数说明:
(1)shmid:是共享内存的标识码
(2)shmaddr:是shmat()函数返回的指针
4.返回值
成功:返回0
失败:返回-1
(4)shmctl()
1.功能:用于控制共享内存
2.头文件:#include<sys/shm.h>
3.int shmctl(int shmid,int cmd,struct shmid_ds* buf)
参数说明:
(1)shmid:是共享内存的标识码
(2)cmd:将要采取的动作
ps:此参数一般设为IPC_RMID
(3)buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
此参数是如果你想对此数据结构做什么修改的话,就可以设置为自己想要的操作
ps:此参数一般设为NULL
4.返回值
成功:返回0
失败:返回-1
6.示例
实现服务器与客户端之间的通信,客户端循环的依次的往内存中写26个字母,服务器循环的读
代码:
shm.h
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/shm.h>
#include<sys/ipc.h>
#define PATHNAME "."
#define PROJ_ID 0x6666
int createShm(int size);
int getShm(int size);
int destroy(int shmid);
shm.c
#include"shm.h"
int commShm(int size,int flag)
{
key_t key=ftok(PATHNAME,PROJ_ID);
if(key<0)
{
perror("ftok");
exit(1);
}
int shmid=shmget(key,size,flag);
if(shmid<0)
{
perror("shmget");
exit(1);
}
return shmid;
}
int createShm(int size)
{
return commShm(size,IPC_CREAT|IPC_EXCL|0666);
}
int getShm(int size)
{
return commShm(size,0);
}
int destroy(int shmid)
{
if(shmctl(shmid,IPC_RMID,NULL)<0)
{
perror("shmctl");
exit(1);
}
return 0;
}
server.c
#include"shm.h"
int main()
{
//打开共享内存
int shmid=getShm(1024);
int i=0;
//与内存建立连接
char* addr=shmat(shmid,NULL,0);
//睡眠2秒,等待客户端往内存中写东西
sleep(2);
while(i++<26)
{
printf("client say:> %s\n",addr);
sleep(1);
}
//与内存脱离连接
shmdt(addr);
return 0;
}
client.c
#include"shm.h"
int main()
{
//创建共享内存
int shmid=createShm(1024);
//与内存建立连接
char* addr=shmat(shmid,NULL,0);
//睡眠4秒,等待服务器与此内存建立连接
sleep(4);
int i=0;
//打印26个字母
while(i<26)
{
addr[i]='A'+i;
i++;
addr[i]=0;
sleep(1);
}
//脱离与内存的连接
shmdt(addr);
//销毁创建的内存,注意:内存创建了,一定要销毁
destroy(shmid);
return 0;
}
运行结果:
客户端
执行如下的命令:
服务器
编译命令:gcc server.c shm.c -o server
运行结果:
结果解析:
客户端循环的把26个字母写到内存中,因为客户端与服务器共享一个内存,故客户端输入的内容,服务器直接可以输出
现在异常终止程序,再次执行,就会出现如下的结果:
这是因为共享内存的生命周期随内核,如果不显式的删除的话,就会一直存在,所以再次执行程序,就会显式文件已经存在,但是我们可以手动的删除IPC资源
如下:
ipcs -m
//显示所有的共享内存ipcrm -m +shmid
//删除共享内存名为shmid的共享内存
再次执行就可以正常执行了!