共享内存
-
概述
- 共享内存允许两个或多个进程共享物理内存的同一块区域(通常被称为段)。由于一个共享内存段会成为一个进程用户空间的一部分,因此这种IPC机制无需内核介入。所有需要做的就是让一个进程将数据复制进共享内存中,并且这部分数据会对其他所有共享同一个段的进程可用。
- 共享内存是效率最高的IPC技术
-
共享内存使用步骤
-
调用shmget()创建一个新的共享内存段或取得一个既有的共享内存段的标识符(即有其他进程创建的共享内存段)。这个调用将返回后续调用中需要用到的共享内存标识符。
-
使用shmat()来附上这段共享内存,使该段成为调用进程的虚拟内存的一部分。
-
此刻在程序中可以像对待其他可用内存那样对待这个共享内存段。为引用这块共享内存,程序需要使用由shmat()调用返回的addr值,它是一个指向进程的虚拟地址空间中该共享内存段起点的指针。
-
调用shmdt()来分离共享内存段。在这个调用之后,进程就无法再引用这块共享内存了。这一步是可选的,并且在进程终止时会自动完成这一步。
-
调用shmctl()来删除共享内存段。只有当当前所有附加内存段的进程都与之分离之后内存段才会销毁。
-
-
相关函数
头文件:
#include<sys/ipc.h> #include<sys/shm.h>
-
shmget函数
int shmget(key_t key,size_t size,int shmflg);
-
功能:创建一个新的共享内存段,或者获取一个既有的共享内存段标识
新创建的内存段中的数据都会被初始化为0
-
参数:
-key: key_t类型是一个整型,通过这个找到或创建共享内存
一般使用16进制表示,非0值
-size: 共享内存的大小,不能为0
-shmflg:共享内存的属性
-访问权限
-附加属性:创建/判断共享内存是否存在
创建:IPC_CREAT
判断共享内存段是否存在:IPC_EXCL,需要和IPC_CREAT一起使用:
IPC_CREAT | IPC_EXCL | 0664
-
返回值:
成功:返回共享内存的引用的ID(后面对共享内存的操作都是通过这个值来进行的)
失败:返回-1,并设置errno
-
-
shmat函数
void *shmat(int shmid,const void *shmaddr,int shmflg);
-
功能:和当前的进程关联
-
参数:
-shmid:共享内存的标识(ID),由shmget获得
-shmaddr:申请的共享内存的起始地址,指定为NULL,由内核指定
-shmflg:对共享内存的操作
SHM_RDONLY:读权限,要操作共享内存必须有读权限
0:设置为0,系统会默认为读和写权限
-
返回值:
成功:返回共享内存的首地址
失败:返回(*void)-1
-
-
shmdt函数
int shmdt(const void *shmaddr);
- 功能:解除当前进程和共享内存的关联
- 参数:shmaddr:共享内存的首地址
- 返回值:成功返回0,失败返回-1
-
shmctl函数
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
-
功能:删除共享内存,共享内存要删除才会消失,创建共享内存的进程被销毁了对共享内存没有任何影响
-
参数:
-shmid:共享内存的ID
-cmd:要做的操作
IPC_STAT:获取共享内存当前的状态
IPC_SET:设置共享内存的状态
IPC_RMID:标记共享内存被摧毁
-buf:需要设置或获取的共享内存的属性信息
IPC_STAT: buf存储数据
IPC_SET: buf中需要初始化数据,设置到内核中
IPC_RMID:没有用,NULL
struct shmid_ds { struct ipc_perm shm_perm; /* Ownership and permissions */ size_t shm_segsz; /* Size of segment (bytes) */ time_t shm_atime; /* Last attach time */ time_t shm_dtime; /* Last detach time */ time_t shm_ctime; /* Last change time */ pid_t shm_cpid; /* PID of creator */ pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */ shmatt_t shm_nattch; /* No. of current attaches */ ... };
-
利用共享内存实现进程间的通信:
shm_write.c
#include<stdio.h> #include<sys/ipc.h> #include<sys/shm.h> #include<string.h> #include<stdlib.h> int main(){ //创建共享内存 int shmid = shmget(100,4096,IPC_CREAT | 0664); if(shmid==-1){ perror("shmget"); exit(-1); } printf("shmid:%d\n",shmid); //和当前进程关联 void *ptr = shmat(shmid,NULL,0); char *str="hello,world"; //写数据 memcpy(ptr,str,strlen(str)+1); printf("按任意键继续...\n"); getchar(); //解除关联 shmdt(ptr); //删除共享内存 shmctl(shmid,IPC_RMID,NULL); return 0; }
shm_read.c
#include<stdio.h> #include<sys/ipc.h> #include<sys/shm.h> #include<string.h> #include<stdlib.h> int main(){ //获取一个共享内存 int shmid = shmget(100,0,IPC_CREAT); if(shmid==-1){ perror("shmget"); exit(-1); } printf("shmid:%d\n",shmid); //和当前进程关联 void *ptr = shmat(shmid,NULL,0); if(ptr==(void*)-1){ perror("shmat"); exit(-1); } //读数据 printf("%s\n",(char*)ptr); printf("按任意键继续...\n"); getchar(); //解除关联 shmdt(ptr); //删除共享内存 shmctl(shmid,IPC_RMID,NULL); return 0; }
-
ftok函数
key_t ftok(const char *pathname,int proj_id);
-
功能:根据指定的路径名和int值,生成一个共享内存的key
-
参数:
-pathname:指定一个存在且可访问的路径
-proj_id: int类型的值,但是系统调用只会使用其中的1个字节
-
返回值:
成功:返回生成的key
失败:返回-1,并设置errno
-
-
-
共享内存操作命令
- ipcs命令
ipcs -a
打印当前系统中所有进程间通信方式的信息ipcs -m
打印出使用共享内存进行进程间通信的信息ipcs -q
打印出使用消息队列进行进程间通信的信息ipcs -s
打印出使用信号进行进程间通信的信息
- ipcrm命令
ipcrm -M shmkey
移除用shmkey创建的共享内存段ipcrm -m shmid
移除用shmid标识的共享内存段ipcrm -Q msqkey
移除用msqkey创建的共享内存段ipcrm -q msqid
移除用msqid标识的共享内存段ipcrm -S semkey
移除用semkey创建的共享内存段ipcrm -s semid
移除用semid标识的共享内存段
- ipcs命令
-
共享内存和内存映射的区别:
-
共享内存可以直接创建,内存映射需要磁盘文件(匿名映射除外)
-
共享内存效率更高
-
内存
- 共享内存:所有进程操作的都是同一块共享内存
- 内存映射:每个进程都在自己的虚拟地址空间中有一个独立的内存
-
数据安全
-
进程突然退出:
-共享内存还存在
-内存映射区消失
-
运行进程的电脑死机、宕机:
-共享内存中的数据就没有了
-内存映射中的数据,由于磁盘文件中的数据还存在,所以内存映射区的数据还存在
-
-
生命周期
- 内存映射:进程退出,内存映射区销毁
- 共享内存:进程退出,共享内存还存在,标记删除(所有的关联的进程数为0)或者关机,后共享内存才会释放。如果一个进程退出,会自动和共享内存进行取消关联
-