一、systemv版本共享内存
进程间通信的本质是:先让不同的进程,看到同一份资源!
1.1直接原理
两个进程
1.1.2原理描述
有A、B两个进程,他们都够task_struct的结构体,task_struct内指向当前进程的进程地址空间,这是前置基础。首先是通过系统调用接口向操作系统申请内存,拿到key和shmid。然后将当前内存的物理地址分别向两个进程的页表进行挂载映射。形成与之映射的虚拟地址,然后在用户区开辟一段空间。当页表有虚拟地址时,会将用户区开辟的这段空间的虚拟地址返回到task_struct,这样两个进程就可以通过虚拟地址访问到相同的内存,那么种内存就是共享内存,并通过不同进程的读写操作实现通信。
1.1.2如何释放共享内存:
去关联的挂载,再释放共享内存
1.1.3那么内存的申请,挂接,关联这些直接操作是进程做的?
直接由操作系统来做的。可以将进程理解为需求方,操作系统为执行方。执行方通过系统调用直接完成如上的操作。
1.1.4问题来了,进程申请的共享内存越多,要不要管理所有的共享内存呢??
先描述,再组织,其实共享内存也会被操作系统进行先描述后组织。比如操作系统会封装一个结构体里面包含了统计当前有多少个共享内存,共享内存的大小,共享内存有多少个挂载等等这些信息,然后通过写函数的形式进行重载。
1.2理解key:
1.key是一个数字,数字是多少不重要。关键在于它必须在内核中具有唯一性,能够让不同的进程进行唯一性标识
2.第一个进程可以通过key创建共享内存,第二个如果拿到和第一个进程相同的key,也就是可以看到同一份资源,也就是意味着他们两个进程可以进行通信。
3.我认为key是操作系统底层进行唯一标识的东西,那么进程如何取找到这个相对唯一的值呢,应用层应该是用shmid。
二、创建共享内存
2.1认识系统接口ftok
2.1.1为什么要使用ftok?
我们如何得到一个在内核中操作系统能唯一标识得key,那么就不得不说说ftok接口得作用。ftok – 将路径名和项目标识符转换为 System V IPC 密钥。因为系统路径是肯定具有绝对得唯一性,从而进行加密返回后的key肯定也是具有唯一性。
2.1.2ftok的返回值
ftok的返回值类型是key_t 也就是说返回值成功就是key,失败返回-1。
2.1.3ftok的参数
第一个为文件的路径,第二个proj_id
为指定的数字,也就是在proj_id
相同的情况下,只要文件(或目录)名称不变,就可以确保ftok返回始终一致的键值。
2.2认识系统接口shmget
2.2.1shmget的作用
shmget系统接口可以让我们获得应用层进程能操作的shmid。shmid也具有唯一性。
2.2.2shmget的参数理解
key就是我们通过ftok函数生成的具有唯一性的键值,size表示共享内存的大小,这个size可以我们自己进行设定。最后一个shmflg,有两个参数可以选择。
第一种:IPC_CREAT
如果key没有被创建共享内存,进行创建并返回shmid。并且如果已经创建并存在就返回当前key对应的shmid。
第二种:IPC_CREAT | IPC_EXCL
如果key没有被创建共享内存,进行创建并返回shmid。并且如果已经创建并存在就返回小于0的数,并返回错误码被更新。
所以通常会使用第一种shmflg的参数。
2.2.3shmget的返回值
shmget的返回值如果共享内存创建成功,错误码没有被更新,返回该共享内存的shmid,如果错误码被更新表示创建失败,返回负数。
int shmid=shmget(k,size,IPC_CREAT | 0664);
if(shmid
2.3挂载shmat
2.3.1如何理解挂载呢?
挂载可以理解为将我们创建的共享内存和不同的进程进行内存映射的关系。并拿到可以进行读和写的虚拟地址。
2.3.2shmat的参数理解
第一个参数就是我们的shmid,这个肯定是唯一被标识的。其次是shamddr,这个如果是nullptr,那么操作系统会选择一个能挂载的地址当作返回值进行返回。同时最后一个参数shmflg一般传入一个0即可。
2.3.3shmat的返回值
返回值的类型是void*,那么一般在编写代码的时候会进行强制类型转换。如下:
//shmat 的返回值类型为void* 挂载
char* shmaddr=(char*)shmat(shmid,nullptr,0);
2.4进行通信
到这里我们就可以进行通信了。那么如何进行通信,无非就是发送数据,接收数据。
很简单,比如我们通过上面的shmat进行了挂载获得了共享内存的虚拟地址。那么我们在这个虚拟地址内写东西和读东西即可。
2.4.1读端代码:
char* shmaddr=(char*)shmat(shmid,nullptr,0);
cout
2.4.2写端代码:
//shmat 的返回值类型为void*
char* shmaddr=(char*)shmat(shmid,nullptr,0);
fgets(shmaddr,size,stdin);
2.5认识共享内存的基本属性shmctl
接口
首先我们来认识一下这个接口的作用。
之前介绍了因为如果向操作系统申请了很多的共享内存,共享内存又进行了很多的连接,操作系统其实是会对共享内存进行管理的。那么我们是如果能看到他被管理了。我们可以调用shmctl接口实现查看唯一shmid对应的共享内存的基本属性。
2.5.1了解shmctl的参数
- 第一个参数就是熟悉的shmid,这个是找到我们想查看的共享内存唯一标识,因为shmid和key是有映射关系,所以底层操作系统是通过这个映射关系找到key,从而确定这块儿共享内存。
- 第二个参数就是cmd,也就是命令,通常使用IPC_STAT:读取共享内存区的shmid_ds结构,并将其存储到buf指向的地址中。
- 第三个参数是buf,也就是会将获得shmid_ds存入buf中。
2.5.2来看看shmid_ds结构体长什么样
这里面包含了共享内存的基本属性:
- 比如最新挂载连接时间shm_atime
- 最新结束挂载连接时间shm_dtime
- 最新改变的时间shm_ctime
- 创建这个共享内存的进程编号shm_cpid
- 最新挂载的进程和结束挂载的进程shm_lpid
- 标志当前没有挂载shm_nattch
- 一个数据块的大小shm_segsz
如下是一段调用shmctl获取共享内存的属性数据,注已经获得了shmid
//共享内存就是一块内存,所以它肯定会被管理起来,所以就能获得他的基本属性
shmctl(shmid,IPC_STAT,&shmds);
cout
2.6结束挂载shmdt
我们一般选择下面这种结束挂载的方式,应为在结束挂载之前我们是知道共享内存的虚拟地址。
//结束挂载
shmdt(shmaddr);
三、共享内存的完整代码实现
3.1processa.cc
#include"comm.hpp"
//shmget 系统调用的接口 申请共享内存 返回共享内存的标识符 失败返回-1
int main()
{
//应用层 用的都服务器托管网是shmid 底层操作系统标识的是key
int shmid=GetShareMem();
//cout
3.2processb.cc
#include"comm.hpp"
int main()
{
int shmid=GetShm();
log(Debug,"create shm done");
//shmat 的返回值类型为void*
char* shmaddr=(char*)shmat(shmid,nullptr,0);
log(Debug,"attach shm done,shmaddr :0x%x",shmaddr);
//ipc通信
while(true)
{
cout
3.3 comm.hpp
#ifndef _COMM_HPP_
#define _COMM_HPP_
#include
#include
#include
#include"log.hpp"
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int size=4096;
const string pathname="/home/hsq/11_18_1";
const int proj_id=0x0664;
Log log;
key_t GetKey()
{
key_t k=ftok(pathname.c_str(),proj_id);
if(k
3.4log.hpp
#ifndef _COMM_HPP_
#define _COMM_HPP_
#include
#include
#include
#include"log.hpp"
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int size=4096;
const string pathname="/home/hsq/11_18_1";
const int proj_id=0x0664;
Log log;
key_t GetKey()
{
key_t k=ftok(pathname.c_str(),proj_id);
if(k
3.5makefile
.PHONY:all
all:processa processb
processa:processa.cc
g++ -o $@ $^ -g -std服务器托管网=c++11
processb:processb.cc
g++ -o $@ $^ -g -std=c++11
.PHONY:clean
clean:
rm -f processa processb
3.6运行结果
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net