目录
为什么有system V
共享内存
原理
操作
shmget 创建共享内存
shmctl 控制共享内存
shmat 挂接共享内存到进程的虚拟地址空间中
shmdt 将共享内存去关联
特点
模拟练习
Makefile
client.cpp
server.cpp
main.hpp
小知识
为什么有system V
linux是一种类unix系统,管道(文件级进程间通信方案)只对于linux来说合适,对于其他类unix系统不一定适合,所以要有系统级别的进程间通信方案,就是system V
System V 分三种 :共享内存 消息队列 信号量
共享内存
原理
在物理内存中开辟内存块,将物理内存地址映射页表到两个进程的虚拟地址空间中,这样两个进程就都可以看到这份资源,满足进程间通信条件.
操作
shmget 创建共享内存
参数中:
size: 表示共享内存的大小,以字节为单位(建议是4096的整数倍)
key: 1.是用户传入的一个用来区分不同共享内存的键值(给操作系统使用)
2.用户可以通过ftok系统调用,通过算法形成一个key(将用户传入的pathname和id合并),用户可
以随便传入,只需保证不重复就可以.
shmflg: 共享内存的标志位(传宏),同时可以传权限
创建用的宏有两个:
1.IPC_CREAT 单独使用,如果创建的共享内存不存在,创建,存在就获取shmid并返回
2.IPC_EXCL 单独使用无意义
两者组合使用:如果共享内存不存在,创建,存在,出错返回
返回值:
成功返回合法的内存标识符(shmid),失败返回-1
shmid: 供用户使用的标识共享内存的标识符
shmctl 控制共享内存
参数:
shmid: 内存标识符,用户使用的
cmd: 传宏表示功能使用
常用:
删除传IPC_RMID
获取传IPC_STAT
buf: 一个输出型参数,当获取共享内存时使用,共享内存的数据就会被保存到这个shmid_ds结构体对象中.
返回值:
失败返回-1(并设置errno错误信息),成功返回值0
shmat 挂接共享内存到进程的虚拟地址空间中
参数:
shmaddr:指明共享内存映射到虚拟地址空间的位置,一般不需要自己指明。
shmflg: 指定进程对共享内存的访问权限,一般传0缺省使用该共享内存的权限。
返回值:
成功返回共享内存映射的虚拟地址,失败返回(void*)-1
判断时要强转,记得64位系统下,void*指针为8字节,所以int强转会损失数据,使用long long int强转
shmdt 将共享内存去关联
返回值:
成功返回0,失败返回-1
特点
1.共享内存默认映射到共享区,访问时不需要系统调用,直接向虚拟地址写入就行.
2.生命周期不随进程,进程结束不会自动回收,随内核
3.使用共享内存时,一定是一个进程创建,一个进程获取,否则两方都去创建,无法通信。
4.是所有IPC中速度最快的,直接向虚拟地址进行写入写出,即减少了数据拷贝次数,又无需使用系统调用(系统调用是有成本的)
5.缺点:没有同步/互斥机制,来对多个进程的访问进行协同,会带来并发问题,
模拟练习
Makefile
.PHONY:all
all:server client
server:server.cpp
g++ -o $@ $^ -std=c++11
client:client.cpp
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f server client
client.cpp
#include "main.hpp"
int main()
{
Shm c_shm;
c_shm.create();
c_shm.attach();
while(1)
{
char ch;
cout<<"请写入字符:";
cin>>ch;
c_shm.add(ch);
}
c_shm.detach();
return 0;
}
server.cpp
#include "main.hpp"
int main()
{
Shm s_shm;
s_shm.create();
s_shm.attach();
while(1)
{
char ch = '0';
bool read_ret = s_shm.read(ch);
if(read_ret)
cout<<"读到了:"<<ch<<endl;
}
s_shm.detach();
return 0;
}
main.hpp
#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
using namespace std;
#define ERR_EXIT(x)\
do{\
perror(x);\
exit(EXIT_FAILURE);\
}while(0)
#define SIZE 4096
#define Mode 0666
class Shm
{
int _shmid;
void * _start_addr;
int _index = 4;
public:
Shm()
{}
bool create()
{
key_t ftokret = ftok("shm",6);
_shmid = shmget(ftokret,SIZE,IPC_CREAT|Mode);
if(_shmid<0)
{
ERR_EXIT("shmget");
}
return true;
}
bool attach()
{
_start_addr = shmat(_shmid,NULL,0);
if((long long int)_start_addr==-1)
{
ERR_EXIT("shmat");
}
return true;
}
bool detach()
{
int shmdtret = shmdt(_start_addr);
if(shmdtret==-1)
{
ERR_EXIT("shmdt");
}
return true;
}
void add(char ch)
{
if(((int*)_start_addr)[0]<SIZE-4)
{
((char*)_start_addr)[_index++] = ch;
(*(int*)_start_addr)++;
}
}
bool read(char& ch)
{
if(_index-4<*(int*)_start_addr)
ch = ((char*)_start_addr)[_index++];
else
return false;
return true;
}
~Shm()
{
int shmctlret = shmctl(_shmid,IPC_RMID,nullptr);
if(shmctlret<0)
{
ERR_EXIT("shmctl_delete");
}
}
};
小知识
1.ipcs -m 命令行指令,查看共享内存
2.ipcrm -m shmid 删除共享内存
3.关于申请空间:内核申请空间必是4kb的整数倍,就算用户开4097个字节,那么内核就会开2*4096个字节大小空间。