文章目录
共享内存
- 共享内存即在物理内存上开辟一块空间,然后多个进程通过页表将这同一个物理内存映射到自己的虚拟地址空间中,通过自己的虚拟地址空间来访问这块物理内存,达到了数据共享的目的。
1 原理图示
2 特点
- 因为这种特性,使得共享内存成为了最快的进程间通信的方式,因为它直接通过虚拟地址来访问物理内存,比前面的管道和后面的消息队列
少了内核态和用户态的几次数据拷贝和交互
。 - 问题在于,当两个或多个进程使用共享内存进行通信时,同步问题的解决显得尤为重要,否则就会造成因不同进程同时读写一块共享内存中的数据而发生混乱。在通常的情况下,通过使用信号量来实现进程的同步。
3 实现共享内存的过程
- 创建共享内存
- 将共享内存映射到虚拟地址空间
- 进行操作
- 解除映射关系
- 释放共享内存
4 共享内存函数
4.1 shmget函数
- 功能:⽤来创建共享内存
- 原型:int shmget(key_t key, size_t size, int shmflg);
- 参数:
key
:这个共享内存段名字
- 使用ftok函数根据路径名和项目id创建一个key,ftok的函数原型为:
1. #include <sys/types.h>
2. #include <sys/ipc.h>
3. key_t ftok(const char *pathname, int proj_id);
size
:共享内存⼤⼩
shmflg
:由九个权限标志构成,它们的⽤法和创建⽂件时使⽤的mode模式标志是⼀样的
- 返回值:成功返回⼀个⾮负整数,即该共享内存段的标识码;失败返回-1。
4.2 shmctl函数
- 功能:⽤于控制共享内存
- 原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
- 参数:
shmid
:由shmget返回的共享内存标识码
cmd
:将要采取的动作(有三个可取值)
IPC_ STAT
:把shmid_ ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值
IPC_ SET
:在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值
IPC_RMID
:删除共享内存段
buf
:指向⼀个保存着共享内存的模式状态和访问权限的数据结构。 - 返回值:成功返回0;失败返回-1
4.3 shmat函数
- 功能:将共享内存段连接到进程地址空间
- 原型:void *shmat(int shmid, const void *shmaddr, int shmflg);
- 参数:
shmid
:共享内存标识
shmaddr
:指定连接的地址
shmflg
:它的两个可能取值是SHM_ RND和SHM_RDONLY - 返回值:成功返回⼀个指针,指向共享内存第⼀个节;失败返回-1
4.4 shmdt函数
- 功能:将共享内存段与当前进程脱离
- 原型:int shmdt(const void *shmaddr);
- 参数:
shmaddr
:由shmat所返回的指针 - 返回值:成功返回0;失败返回-1
- 注意:将共享内存段与当前进程脱离不等于删除共享内存段
5 代码模拟实现内存共享
- 创建两个进程,在 A 进程中创建一个共享内存,并向其写入数据,通过 B 进程从共享内存中读取数据
写端代码:
#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 4096
int main(int argc, char *argv[])
{
//创建key值
key_t key = ftok("../", 2015);
if(key == -1)
{
perror("ftok");
}
//创建共享内存
int shmid = shmget(key, BUFSZ, IPC_CREAT|0666);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
//映射
char* 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 4096
int main(int argc, char *argv[])
{
//创建key值
ket_t key = ftok("../", 2015);
if(key == -1)
{
perror("ftok");
}
system("ipcs -m"); //查看共享内存
//打开共享内存
int shmid = shmget(key, BUFSZ, IPC_CREAT|0666);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
//映射
char *shmadd = shmat(shmid, NULL, 0);
if(shmadd < 0)
{
perror("shmat");
exit(-1);
}
//读共享内存区数据
printf("data = [%s]\n", shmadd);
//分离共享内存和当前进程
int ret = shmdt(shmadd);
if(ret < 0)
{
perror("shmdt");
exit(1);
}
else
{
printf("deleted shared-memory\n");
}
//删除共享内存
shmctl(shmid, IPC_RMID, NULL);
//成功返回0,失败返回-1;
system("ipcs -m"); //查看共享内存
return 0;
}
6 查看系统的共享存储
ipcs
- ipcs -m
-m列出共享内存
-s列出共享信号量
-q列出共享队列
-q
:
-m
:
-s
:
7 删除共享内存资源
ipcrm
- 最常用的是:-m [shmid]
8 内存共享优缺点
- 优点:我们可以看到使用共享内存进行进程间的通信真的是非常方便,而且函数的接口也简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,也
加快了程序的效率
。同时,它也不像匿名管道那样要求通信的进程有一定的父子关系。 - 缺点:共享内存
没有提供同步的机制
,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作。