概念介绍!!!
共享内存,顾名思义就是允许多个不相关的进程可以访问同一个逻辑内存,共享内存是多个正在运行的进程之间共享和传递数据的一种非常有效的方式。操作系统将不同进程之间共享内存安排为同一段物理内存,进程可以将共享内存连接到它们自己的地址空间中,如果某个进程修改了共享内存中的数据,其它的进程读到的数据也将会改变。
共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。如图:
共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
因为多个进程可以同时操作,所以需要进行同步。
信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
#include
#include //头文件
// 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
int shmget(key_t key, size_t size, int flag);
// 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
void *shmat(int shm_id, const void *addr, int flag);
// 断开与共享内存的连接:成功返回0,失败返回-1
int shmdt(void *addr);
// 控制共享内存的相关信息:成功返回0,失败返回-1
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
介绍下相关的函数
原型:int shmget(key_t key, size_t size, int shmflg);
功能:用来获取或创建共享内存
参数:
key:IPC_PRIVATE 或 ftok的返回值
size:需要申请共享内存的大小,以gb为单位。在操作系统中,申请内存的最小单位为页,一页是4k字节,为了避免内存碎片,我们一般申请的内存大小为页的整数倍。
shmflg:如果要创建新的共享内存,需要使用IPC_CREAT,IPC_EXCL,如果是已经存在的,可以使用IPC_CREAT或直接传0。比如IPC_CREAT搭配0666操作权限
返回值:
成功返回shmid——共享内存段标识符
出错返回‐1
原型:void *shmat(int shm_id, const void *shm_addr, int shmflg);
功能:把共享内存连接映射到当前进程的地址空间
参数:
shm_id:创建的id号
shm_addr:映射到的地址,shmaddr = 0,则存储段连接到由内核选择的第一个可以地址上
shmflg:
SHM_RDONLY共享内存只读
默认是0,表示共享内存可读写
返回值:
成功返回共享存储段的指针(虚拟地址),并且内核将使其与该共享存储段相关的shmid_ds结构中的shm_nattch计数器加1(类似于引用计数);出错返回-1。
原型:int shmdt(const void *shmaddr);
功能:将进程里的地址映射删除!!!当一个进程不需要共享内存的时候,就需要去关联。该函数并不删除所指定的共享内存区,而是将之前用shmat函数连接好的共享内存区脱离目前的进程。
参数:
shmid:要操作的共享内存标识符
返回值:
成功:0
出错:‐1
原型:int shmctl(int shm_id, int command, struct shmid_ds *buf);
功能:删除共享内存对象
参数:
shmid:要操作的共享内存标识符
cmd :
一般使用,IPC_RMID (删除对象)
buf :指定IPC_STAT/IPC_SET时用以保存/设置属性,0或者null即可
返回值:
成功:0
出错:‐1
接下来我们看看demo
demoa.c文件
#include
#include
#include
#include
#include
#include
int main(){
int shmid;
char *shmaddr;
char *buf = "hello,i am A";
// key_t ftok(const char *pathname, int proj_id);
key_t key;
key = ftok(".",23);
//create
// int shmget(key_t key, size_t size, int shmflg);
shmid = shmget(key,1024*4,IPC_CREAT|0666);
if(shmid == -1){
printf("create failed!\n");
exit(-1);
}
//mapping
// void *shmat(int shmid, const void *shmaddr, int shmflg);
shmaddr = shmat(shmid,0,0);
printf("shmaddr mapping success!\n");
// char *strcpy(char *dest, const char *src);
strcpy(shmaddr,buf);
sleep(5);
//disconncet
// int shmdt(const void *shmaddr);
shmdt(shmaddr);
//delete
// int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmctl(shmid,IPC_RMID,0);
return 0;
}
demob.c文件
#include
#include
#include
#include
#include
#include
int main(){
int shmid;
char *shmaddr;
// key_t ftok(const char *pathname, int proj_id);
key_t key;
key = ftok(".",23);
//create
// int shmget(key_t key, size_t size, int shmflg);
shmid = shmget(key,1024*4,0);
if(shmid == -1){
perror("because");
printf("create failed!\n");
exit(-1);
}
//mapping
// void *shmat(int shmid, const void *shmaddr, int shmflg);
shmaddr = shmat(shmid,0,0);
printf("shmaddr mapping success!\n");
printf("date send by a:%s\n",shmaddr);
//disconncet
// int shmdt(const void *shmaddr);
shmdt(shmaddr);
//delete
// int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmctl(shmid,IPC_RMID,0);
return 0;
}
demo中A是写入端,B是读取端,在A中约定好物理地址空间,映射到A的地址空间,然后进行数据操作,在这里我们sleep5秒让B有时间读取数据。同样的B执行完操作后,A和B就都关联共享内存,也就是解除映射关系,最后约定删除内存空间。
需要注意这几点:
当用shmget函数创建一段共享内存时,必须指定其 size;而如果引用一个已存在的共享内存,则将 size 指定为0 。
当一段共享内存被创建以后,它并不能被任何进程访问。必须使用shmat函数连接该共享内存到当前进程的地址空间,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问。
shmdt函数是用来断开shmat建立的连接的。注意,这并不是从系统中删除该共享内存,只是当前进程不能再访问该共享内存而已。
shmctl函数可以对共享内存执行多种操作,根据参数 cmd 执行相应的操作。常用的是IPC_RMID(从系统中删除该共享内存)。
这里插一句:
共享内存是IPC中最快的一种,也是常用的方式(搭配信号)
A 进程给内存中写数据,B 进程从内存中读取数据,在此期间一共发生了两次复制(1)A 到共享内存 (2)共享内存到 B
因为直接在内存上操作,所以共享内存的速度也就提高了。
最后补充下命令
systemV标准的三种查看进程间通信资源的命令:
ipcs 三种全部查看
-m 查看共享内存
-q 查看消息队列
-s 查看信号量数组
ipcrm 删除进程间通信资源
-m -q -s 和上面一样