共享内存指在多处理器的计算机系统中,可以被不同中央处理器访问的大容量内存。由于CPU需要快速访问存储器,这样就要对存储器进行缓存。任何一个缓存的数据被更新后,由于其他处理器也可能要存取,共享内存就需要立即更新,否则不同的处理器可能用到不同的数据。
1.共享内存概述
共享内存是进程间通信中最简单的方式之一。共享内存允许两个及以上的进程同时访问同一块内存。共享内存实际上就是进程通过调用shmget来分配一个共享内存块,然后进程通过shmat绑定到共享内存块,将进程的逻辑虚拟地址空间指向共享内存块中去。
特点:共享内存是进程间通信追快的一种方法。一个进程向共享内存中写入数据,其他与之绑定的进程都能立刻看到其中的内容。
使用共享内存要注意是多个进程之间在使用这片内存时是互斥的,当一个进程正在向共享内存区写入数据时,则在完成这一操作前,其他进程不能去读写这写数据。
2.共享内存的通信
因为所有进程共享同一块内存,共享内存在各种进程间通信方式中具有最高的效率。访问共享内存区域和访问进程独有的内存区域一样快,并不需要通过系统调用或者其它需要切入内核的过程来完成。同时它也避免了对数据的各种不必要的复制。
因为系统内核没有对访问共享内存进行同步,您必须提供自己的同步措施。例如,在数据被写入之前不允许进程从共享内存中读取信息、不允许两个进程同时向同一个共享内存地址写入数据等。解决这些问题的常用方法是通过使用信号量进行同步。
3.共享内存的内存模型
要使用一块共享内存,进程必须首先分配它。随后需要访问这个共享内存块的每一个进程都必须将这个共享内存绑定到自己的地址空间中。当完成通信之后,所有进程都将脱离共享内存,并且由一个进程释放该共享内存块。理解 Linux 系统内存模型可以有助于解释这个绑定的过程。在 Linux 系统中,每个进程的虚拟内存是被分为许多页面的。这些内存页面中包含了实际的数据。每个进程都会维护一个从内存地址到虚拟内存页面之间的映射关系。尽管每个进程都有自己的内存地址,不同的进程可以同时将同一个内存页面映射到自己的地址空间中,从而达到共享内存的目的。
分配一个新的共享内存块会创建新的内存页面。因为所有进程都希望共享对同一块内存的访问,只应由一个进程创建一块新的共享内存。再次分配一块已经存在的内存块不会创建新的页面,而只是会返回一个标识该内存块的标识符。一个进程如需使用这个共享内存块,则首先需要将它绑定到自己的地址空间中。这样会创建一个从进程本身虚拟地址到共享页面的映射关系。当对共享内存的使用结束之后,这个映射关系将被删除。当再也没有进程需要使用这个共享内存块的时候,必须有一个(且只能是一个)进程负责释放这个被共享的内存页面。
共享内存的分配(shared memory get)
int shmget(key_t key,size_t size,int shmflg)
功能:创建打开一块共享内存
参数:key进程间通信键值,size共享存储区的字节长度,shmflg标识函数的行为及共享内存权限
IPC_CREAT如果不存在就创建,IPC_EXCL如果已经存在返回失败
返回值:成功返回标识符,失败返回-1。
共享内存的映射(shared memory attach)
void*shmat(int shmid,const void *shmddr,int shmflg)
功能:将一个共享内存段映射到调用进程的数据段中。
参数:shmid共享内存标识符,shmaddr共享内存映射地址,shmflg:共享内存段的访问权限和映射条件( 通常为 0 )。
返回值:成功返回共享内存段映射地址,失败返回-1。
解除共享内存的映射(shared memory detach)
int shmdt(const void *shmaddr)
功能:将共享内存和当前进程分离。
参数:shmaddr:共享内存映射地址。
返回值:成功返回0,失败返回-1。
共享内存的控制(shmctl)
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
功能:共享内存属性的控制。
参数:shmid共享内存标识符。cmd函数功能的控制(IPC_RMID)。buf数据类型的地址(具体类型请点此链接 ),用来存放或修改共享内存的属性。
返回值:成功返回0,失败返回-1。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define BUFSZ 512
int main(int argc, char *argv[])
{
int shmid;
int ret;
key_t key;
char *shmadd;
//创建key值
key = ftok("../", 2015);
if(key == -1)
{
perror("ftok");
}
//创建共享内存
shmid = shmget(key, BUFSZ, IPC_CREAT|0666);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
//映射
shmadd = shmat(shmid, NULL, 0);
if(shmadd < 0)
{
perror("shmat");
_exit(-1);
}
//拷贝数据至共享内存区
printf("copy data to shared-memory\n");
bzero(shmadd, BUFSZ); // 共享内存清空
strcpy(shmadd, "how are you, lh\n");
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define BUFSZ 512
int main(int argc, char *argv[])
{
int shmid;
int ret;
key_t key;
char *shmadd;
//创建key值
key = ftok("../", 2015);
if(key == -1)
{
perror("ftok");
}
system("ipcs -m"); //查看共享内存
//打开共享内存
shmid = shmget(key, BUFSZ, IPC_CREAT|0666);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
//映射
shmadd = shmat(shmid, NULL, 0);
if(shmadd < 0)
{
perror("shmat");
exit(-1);
}
//读共享内存区数据
printf("data = [%s]\n", shmadd);
//分离共享内存和当前进程
ret = shmdt(shmadd);
if(ret < 0)
{
perror("shmdt");
exit(1);
}
else
{
printf("deleted shared-memory\n");
}
//删除共享内存
shmctl(shmid, IPC_RMID, NULL);
system("ipcs -m"); //查看共享内存
return 0;
}