Linux一学就会——共享内存
system V共享内存
共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到
内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。
管道通信里面我们知道,要是想进行通讯就必须让两个进程看到同一片内存区域(其实也就是最开始共享内存的雏形)。
原理就不用多讲了,之前管道通信是因为进程和子进程天然的可以看到共同的内存区域。这一次我们可以创建一个通向内存让非子进程也可以看到同一片区域进行通信。复习一下管道通信
共享内存函数
shmget函数
功能:用来创建共享内存
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key:这个共享内存段名字
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1
shmat函数
功能:将共享内存段连接到进程地址空间
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid: 共享内存标识
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函数
功能:将共享内存段与当前进程脱离
原型
int shmdt(const void *shmaddr);
参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段
shmctl函数
功能:用于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1
标题makefile文件
.PHONY:all
all:shmclient shmserver
shmclient:client.cc
g++ -o $@ $^ -std=c++11
shmserver:server.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f shmserver shmclient
comm.h
#ifndef __COMM_HPP__
#define __COMM_HPP__
#include <iostream>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <cassert>
#include <string>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
using namespace std;
// IPC_CREAT and IPC_EXCL
// 单独使用IPC_CREAT: 创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回
// IPC_EXCL不能单独使用,一般都要配合IPC_CREAT
// IPC_CREAT | IPC_EXCL: 创建一个共享内存,如果共享内存不存在,就创建之, 如果已经存在,则立马出错返回 -- 如果创建成功,对应的shm,一定是最新的!
#define PATHNAME "."
#define PROJ_ID 0x6636
const int gsize = 4096; //暂时
key_t getKey()
{
key_t k = ftok(PATHNAME, PROJ_ID);//ftok生成特殊数字,结合了path和proj的特性生成的独一无二的数字
if(k == -1)
{
cerr << "error: " << errno << " : " << strerror(errno) << endl;
return 1;
}
return k;
}
string toHex(int x)
{
char buffer[64];
snprintf(buffer, sizeof buffer, "0x%x", x);
return buffer;
}
static int createShmHelper(key_t k, int size, int flag)//key理论上是任意值,但是我们为了严谨 要用ftok生成一个独一无二的数值,这个其实就是酒店的房间号,两个小情侣记住这个房间号才能约会,不然没法约会,这是两个人的暗号
{
int shmid = shmget(k, gsize, flag);//申请一个内存块,用来当作共享内存,gsize是大小
if(shmid == -1)
{
cerr << "error: " << errno << " : " << strerror(errno) << endl;
return 2;
}
return shmid;
}
int createShm(key_t k, int size)
{
umask(0);
return createShmHelper(k, size, IPC_CREAT | IPC_EXCL | 0666);
}
int getShm(key_t k, int size)
{
return createShmHelper(k, size, IPC_CREAT);
}
char* attachShm(int shmid)
{
char *start = (char*)shmat(shmid, NULL, 0);
return start;
}
void detachShm(char *start)
{
int n = shmdt(start);
assert(n != -1);
(void)n;
}
void delShm(int shmid)
{
int n = shmctl(shmid, IPC_RMID, NULL);
assert(n != -1);
(void)n;
}
#define SERVER 1
#define CLIENT 0
class Init
{
public:
Init(int t):type(t)
{
key_t k = getKey();
if(type == SERVER) shmid = createShm(k, gsize);
else shmid = getShm(k, gsize);
start = attachShm(shmid);
}
char *getStart(){ return start; }
~Init()
{
detachShm(start);
if(type == SERVER) delShm(shmid);
}
private:
char *start;
int type; //server or client
int shmid;
};
#endif
client.cc
#include "comm.hpp"
#include <unistd.h>
#include<string>
using namespace std;
int main()
{
Init init(CLIENT);
char *start = init.getStart();
string str="I am process A";
int len=str.size();
int i=0;
// start=str;
while(i<len)
{
start[i] = str[i];
i++;
start[i] = '\0';
sleep(1);
}
// key_t k = getKey();
// cout << "client key: " << toHex(k) << endl;
// int shmid = getShm(k, gsize);
// cout << "client shmid: " << shmid << endl;
// //3. 将自己和共享内存关联起来
// char* start = attachShm(shmid);
// sleep(15);
// // 4. 将自己和共享内存去关联
// detachShm(start);
return 0;
}
server.cc
#include "comm.hpp"
#include <unistd.h>
#include<string>
using namespace std;
int main()
{
Init init(SERVER);
char *start = init.getStart();
int n = 0;
while(n <= 30)
{
cout <<"client -> server# "<< start << endl;
sleep(1);
n++;
}
// //1. 创建key
// key_t k = getKey();
// cout << "server key: " << toHex(k) << endl;
// //2. 创建共享内存
// int shmid = createShm(k, gsize);
// cout << "server shmid: " << shmid << endl;
// sleep(3);
// //3. 将自己和共享内存关联起来
// char* start = attachShm(shmid);
// sleep(20);
// // 通信代码在这里!
// // 4. 将自己和共享内存去关联
// detachShm(start);
// sleep(3);
// struct shmid_ds ds;
// int n = shmctl(shmid, IPC_STAT, &ds);
// if(n != -1)
// {
// cout << "perm: " << toHex(ds.shm_perm.__key) << endl;
// cout << "creater pid: " << ds.shm_cpid << " : " << getpid() << endl;
// }
// ?. 删除共享内存
//delShm(shmid);
return 0;
}
执行客户端:
执行服务端:
收到客户发来的信息!
ctrl+c终止进程,再次重启