什么是共享内存
共享内存是能够同时被多个进程看到的一段物理内存。(注意这里是物理内存)。
共享内存的原理
1.先在物理内存中申请一块内存;
2.将这段物理内存通过页表映射到多个进程的虚拟地址空间中;
3.一个进程往自己的虚拟地址空间上写就相当于往物理内存里写,其它进程从自己的虚拟地址空间里读就相当于从那段相同的物理内存里读;
4.这段公共的物理内存就是多个进程看到的同一个资源,这段物理内存也就是共享内存。
共享内存的特点
1.共享内存用于任意多个进程(只要有相同的key,这里的key与消息队列里面key的得到方式一致:https://blog.csdn.net/xu1105775448/article/details/80743938);
2.共享内存可以进行双向通信(全双工);
3.共享内存的生命周期随内核;
4.共享内存不带同步与互斥;
5.共享内存没有面向字节流与面向数据报的概念,因为其只是一段内存,支持随机访问。
6.共享内存是进程间通信方式中效率最高的,因为它直接可以从内存中读取数据,不需要借助其它外界资源。
共享内存的相关API
1.申请一段物理内存(创建一段物理内存)
(1)通过shmget函数
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
(2)参数解析:
key:通过ftok函数获得,是IPC对象里面的那个key,是IPC对象的唯一标识符。
//ftok函数
key_t ftok(const char *pathname, int proj_id);
size:指定申请内存的大小,申请的内存按照4k为单位,采用向上取整的方式(例如申请4097个字节,其实是4k多一点,就会分配8k)。
shmflg:当参数为IPC_CREAT时表示不存在就创建,存在就打开;当参数为IPC_CREAT | IPC_EXCL表示不存在就创建,存在就出错。
(3)示例:
#include<stdio.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/types.h>
#include<sys/shm.h>
#define PATHNAME "." //.表示当前目录
#define PROJ_ID 1
intShmCreate(int size)
{
key_t key = ftok(PATHNAME,PROJ_ID);
if(key < 0)
{
perror("ftok");
return -1;
}
int shmid = shmget(key,size,IPC_CREAT | IPC_EXCL | 0666);
if(shmid < 0)
{
perror("shmget");
return -1;
}
return shmid;
}
2.销毁(释放)这段物理内存
(1)通过shmctl函数
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
(2)参数解析:
shmid:表示共享内存的标识(句柄)
cmd:表示相关对共享内存的操作(指令)
buf:如果cmd为删除共享内存,则可以填NULL
(3)示例:
int ShmDestroy(int shmid) //删除指定的共享内存
{
int ret = shmctl(shmid,IPC_RMID,NULL);
if(ret < 0)
{
perror("shmctl");
return -1;
}
return 0;
}
3.将共享内存连接(映射)到进程的虚拟地址空间(attach)
(1)通过shmat函数
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
(2)参数解析:
shmid:共享内存的唯一标识
shmaddr:表示指定的attach的虚拟地址(为了防止内存访问错误,这个一般由操作系统指定,所以填NULL)
shmflg:有两个取值:SHM_RDONLY和SHM_RND
(3)示例:
char* ptr = (char*)shmat(shmid,NULL,0);
shmat的作用相当于malloc,返回值类型为void*,返回值ptr就是指向虚拟地址空间的那个指针,通过对ptr的操作也就等于对该虚拟地址进行相关操作。
4.将共享内存与虚拟地址空间解除连接(或称去关联)
(1)通过shmdt函数
int shmdt(const void *shmaddr);
(2)参数解析:
shmaddr:就是通过shmat的返回值,也就是执行虚拟地址空间的那个指针
(3)示例:
shmdt(ptr);
shmdt的作用相当于free,参数为一个指针(指向虚拟地址空间的那个指针)
通过命令查看共享内存和删除共享内存
1.查看共享内存
ipcs -m
2.删除共享内存
ipcrm -m 131072 //131072表示某个共享内存的shmid
共享内存的应用
1.题目:利用共享内存实现:一个Write往共享内存里写A,写A的次数按照每秒的时间递增(即第一次是一个A,第二次是两个A…),一个reader从共享内存里面读数据
2.代码:
comm.h
#ifndef __COMM_H__ #define __COMM_H__#include<stdio.h>#include<unistd.h>#include<sys/ipc.h>#include<sys/types.h>#include<sys/shm.h>#define PATHNAME "."#define PROJ_ID 1int ShmCommon(int size,int flags);//在物理内存里面创建一段内存int ShmCreate(int size);//拿到这段物理内存int GetShm(int size);int ShmDestroy(int shmid);#endif
comm.c
#include"comm.h"
//size表示空间的大小
int ShmCommon(int size,int flags)
{
key_t key = ftok(PATHNAME,PROJ_ID);
if(key < 0)
{
perror("ftok");
return -1;
}
int shmid = shmget(key,size,flags);
if(shmid < 0)
{
perror("shmget");
return -1;
}
return shmid;
}
//创建共享内存
int ShmCreate(int size)
{
return ShmCommon(size,IPC_CREAT | IPC_EXCL | 0666);
}
int GetShm(int size)
{
return ShmCommon(size,IPC_CREAT);
}
int ShmDestroy(int shmid)
{
int ret = shmctl(shmid,IPC_RMID,NULL);
if(ret < 0)
{
perror("shmctl");
return -1;
}
return 0;
}
Writer.c
#include"comm.h"
int main()
{
//创建共享内存
int shmid = ShmCreate(1024);
if(shmid < 0)
{
perror("ShmCreate");
return -1;
}
//将当前进程的虚拟地址空间与共享内存进行attach
//shmat的作用相当于malloc
char* ptr = (char*)shmat(shmid,NULL,0);
//当前进程一直往内存里面写入东西
while(1)
{
*ptr = 'A';
++ptr;
*ptr = '\0'; //必须保证字符串是C形式的字符串,以'\0'结束
sleep(1);
}
//shmdt相当于free
shmdt(ptr);
ShmDestroy(shmid);
return 0;
}
reader.c
#include"comm.h"
int main()
{
//打开这段内存
int shmid = GetShm(0);
if(shmid < 0)
{
perror("GetShm");
return -1;
}
//与共享内存建立连接
char* ptr = (char*)shmat(shmid,NULL,0);
//一直从内存中读
while(1)
{
printf("%s\n",ptr);
sleep(1);
}
shmdt(ptr);
return 0;
}