一、什么是共享内存
共享内存区是最快的(进程间通信)IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。注意:共享内存没有进行同步与互斥!共享内存不会自动销毁,要手动销毁。
二、 共享内存的原理图
三、共享内存的接口(什么用)
ftok
功能:生成一个key,key是shmget第一个参数,这个key是一个约定的数,让不同的进程通过key找到同一份资源,不用再进入内存查找。
头文件
#include <sys/types.h>
#include <sys/ipc.h>
原型
key_t ftok(const char *pathname, int proj_id);
参数pathname:一个路径字符串,可以随便给
proj_id:一个int数据,可以随便给
返回值:返回key
shmget
功能:用来创建共享内存
头文件
#include <sys/ipc.h>
#include <sys/shm.h>
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key:这个共享内存段名字,这个key是一个约定的数,让不同的进程通过key找到同一份资源
size:共享内存的大小(一般取 n * 1024)
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的主要用这两个:
IPC_EXCL:不存在共享内存就创建,存在就使用现有的
IPC_EXCL:不存在共享内存就创建,存在就报错,保证创建的共享内存是新的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1
shmat
功能:将共享内存段连接到进程地址空间
头文件
#include <sys/types.h>
#include <sys/shm.h>
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid: 共享内存标识,shmget的返回值
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1说明:
shmaddr为NULL,核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存
shmdt
功能:将共享内存段与当前进程脱离
头文件
#include <sys/types.h>
#include <sys/shm.h>
原型
int shmdt(const void *shmaddr);
参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段,删除共享内存用shmctl
shmctl
功能:用于控制共享内存
头文件
#include <sys/ipc.h>
#include <sys/shm.h>
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
- PC STAT 把shmid ds结构中的数据设置为共享内存的当前关联值
- IPC SET 在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid ds数据结构中给出的值
- IPC RMID 删除共享内存段
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构,一般设为nullptr
返回值:成功返回0;失败返回-1
四、使用演示
使用代码创建一个共享内存, 支持两个进程进行通信
进程A 向共享内存当中写 “i am process A”
进程B 从共享内存当中读出内容,并且打印到标准输出
使用到的linux的一些指令
ipcs -m:查看共享内存的消息
ipcrm -m shmid:删除共享内存标识码为shmid的共享内存
监视脚本
while :; do ipcs -m; sleep 1; done
功能:每隔一秒打印一次共享内存消息
shm.hpp
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string>
#include <cstring>
#include <iostream>
#include <unistd.h>
using namespace std;
const string pathname = "/home/lwj/code11";
const int proj_id = 0x112233;
const int size = 4096;
key_t Getkey()
{
int key = ftok(pathname.c_str(), proj_id);
if(key < 0)
{
perror("ftok");
exit(-1);
}
return key;
}
char* gethex(int x)
{
char s[1024];
sprintf(s, "0x%x", x);
return s;
}
processA.cc
#include "shm.hpp"
int main()
{
key_t key = Getkey();
cout << "获取key: " << gethex(key) << endl;
sleep(10);
//获取shmid
int shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0644);
cout << "创建shm, 获取shmid: " << shmid << endl;
if(shmid < 0)
{
cerr << "shmget fail" << endl;
exit(-1);
};
sleep(10);
//连接shm
cout << "连接shm" << endl;
char* s = (char*)shmat(shmid, nullptr, 0);
sleep(10);
//通信
cout << "写消息" << endl;
string str = "i am process A";
int i = 0;
for (auto e : str)
{
s[i] = e;
i++;
}
s[i] = '\0';
//断开shm
sleep(10);
cout << "断开shm" << endl;
shmdt(s);
sleep(10);
//销毁shm
cout << "销毁shm" << endl;
shmctl(shmid, IPC_RMID, nullptr);
return 0;
}
processB.cc
#include "shm.hpp"
int main()
{
key_t key = Getkey();
cout << "获取key: " << gethex(key) << endl;
sleep(5);
// 获取shmid
int shmid = shmget(key, size, IPC_CREAT);
cout << "创建shm, 获取shmid: " << shmid << endl;
if(shmid < 0)
{
cerr << "shmget fail" << endl;
exit(-1);
}
// 连接shm
cout << "连接shm" << endl;
char *s = (char *)shmat(shmid, nullptr, 0);
sleep(5);
// 通信
cout << "读消息: " << s << endl;
// 断开shm
sleep(10);
cout << "断开shm" << endl;
shmdt(s);
sleep(10);
// 销毁shm
cout << "销毁shm" << endl;
shmctl(shmid, IPC_RMID, nullptr);
return 0;
}
Makefile
.PHNOY:all
all:processA processB
processA:progressA.cc
g++ -o $@ $^ -std=c++11
processB:progressB.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f processA processB
演示效果视频链接:
shm演示视频-CSDN直播